Chore: Vendor wire into pkg/build (#84637)
* vendor latest wire into pkg/build * use vendored wire in builds * fix wire import path * remove wire from bingo * also support google/wire import * make prettier happy * change package in tess * add debug walk for drone * add wire_gen in tests * remove debug walk * restore imports
This commit is contained in:
@@ -0,0 +1,465 @@
|
||||
# Wire User Guide
|
||||
|
||||
## Basics
|
||||
|
||||
Wire has two core concepts: providers and injectors.
|
||||
|
||||
### Defining Providers
|
||||
|
||||
The primary mechanism in Wire is the **provider**: a function that can produce a
|
||||
value. These functions are ordinary Go code.
|
||||
|
||||
```go
|
||||
package foobarbaz
|
||||
|
||||
type Foo struct {
|
||||
X int
|
||||
}
|
||||
|
||||
// ProvideFoo returns a Foo.
|
||||
func ProvideFoo() Foo {
|
||||
return Foo{X: 42}
|
||||
}
|
||||
```
|
||||
|
||||
Provider functions must be exported in order to be used from other packages,
|
||||
just like ordinary functions.
|
||||
|
||||
Providers can specify dependencies with parameters:
|
||||
|
||||
```go
|
||||
package foobarbaz
|
||||
|
||||
// ...
|
||||
|
||||
type Bar struct {
|
||||
X int
|
||||
}
|
||||
|
||||
// ProvideBar returns a Bar: a negative Foo.
|
||||
func ProvideBar(foo Foo) Bar {
|
||||
return Bar{X: -foo.X}
|
||||
}
|
||||
```
|
||||
|
||||
Providers can also return errors:
|
||||
|
||||
```go
|
||||
package foobarbaz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ...
|
||||
|
||||
type Baz struct {
|
||||
X int
|
||||
}
|
||||
|
||||
// ProvideBaz returns a value if Bar is not zero.
|
||||
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
|
||||
if bar.X == 0 {
|
||||
return Baz{}, errors.New("cannot provide baz when bar is zero")
|
||||
}
|
||||
return Baz{X: bar.X}, nil
|
||||
}
|
||||
```
|
||||
|
||||
Providers can be grouped into **provider sets**. This is useful if several
|
||||
providers will frequently be used together. To add these providers to a new set
|
||||
called `SuperSet`, use the `wire.NewSet` function:
|
||||
|
||||
```go
|
||||
package foobarbaz
|
||||
|
||||
import (
|
||||
// ...
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// ...
|
||||
|
||||
var SuperSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz)
|
||||
```
|
||||
|
||||
You can also add other provider sets into a provider set.
|
||||
|
||||
```go
|
||||
package foobarbaz
|
||||
|
||||
import (
|
||||
// ...
|
||||
"example.com/some/other/pkg"
|
||||
)
|
||||
|
||||
// ...
|
||||
|
||||
var MegaSet = wire.NewSet(SuperSet, pkg.OtherSet)
|
||||
```
|
||||
|
||||
### Injectors
|
||||
|
||||
An application wires up these providers with an **injector**: a function that
|
||||
calls providers in dependency order. With Wire, you write the injector's
|
||||
signature, then Wire generates the function's body.
|
||||
|
||||
An injector is declared by writing a function declaration whose body is a call
|
||||
to `wire.Build`. The return values don't matter as long as they are of the
|
||||
correct type. The values themselves will be ignored in the generated code. Let's
|
||||
say that the above providers were defined in a package called
|
||||
`example.com/foobarbaz`. The following would declare an injector to obtain a
|
||||
`Baz`:
|
||||
|
||||
```go
|
||||
// +build wireinject
|
||||
// The build tag makes sure the stub is not built in the final build.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/wire"
|
||||
"example.com/foobarbaz"
|
||||
)
|
||||
|
||||
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
||||
wire.Build(foobarbaz.MegaSet)
|
||||
return foobarbaz.Baz{}, nil
|
||||
}
|
||||
```
|
||||
|
||||
Like providers, injectors can be parameterized on inputs (which then get sent to
|
||||
providers) and can return errors. Arguments to `wire.Build` are the same as
|
||||
`wire.NewSet`: they form a provider set. This is the provider set that gets used
|
||||
during code generation for that injector.
|
||||
|
||||
Any non-injector declarations found in a file with injectors will be copied into
|
||||
the generated file.
|
||||
|
||||
You can generate the injector by invoking Wire in the package directory:
|
||||
|
||||
```shell
|
||||
wire
|
||||
```
|
||||
|
||||
Wire will produce an implementation of the injector in a file called
|
||||
`wire_gen.go` that looks something like this:
|
||||
|
||||
```go
|
||||
// Code generated by Wire. DO NOT EDIT.
|
||||
|
||||
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
|
||||
//+build !wireinject
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"example.com/foobarbaz"
|
||||
)
|
||||
|
||||
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
||||
foo := foobarbaz.ProvideFoo()
|
||||
bar := foobarbaz.ProvideBar(foo)
|
||||
baz, err := foobarbaz.ProvideBaz(ctx, bar)
|
||||
if err != nil {
|
||||
return foobarbaz.Baz{}, err
|
||||
}
|
||||
return baz, nil
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, the output is very close to what a developer would write
|
||||
themselves. Further, there is little dependency on Wire at runtime: all of the
|
||||
written code is just normal Go code, and can be used without Wire.
|
||||
|
||||
Once `wire_gen.go` is created, you can regenerate it by running [`go generate`].
|
||||
|
||||
[`go generate`]: https://blog.golang.org/generate
|
||||
|
||||
## Advanced Features
|
||||
|
||||
The following features all build on top of the concepts of providers and
|
||||
injectors.
|
||||
|
||||
### Binding Interfaces
|
||||
|
||||
Frequently, dependency injection is used to bind a concrete implementation for
|
||||
an interface. Wire matches inputs to outputs via [type identity][], so the
|
||||
inclination might be to create a provider that returns an interface type.
|
||||
However, this would not be idiomatic, since the Go best practice is to
|
||||
[return concrete types][]. Instead, you can declare an interface binding in a
|
||||
provider set:
|
||||
|
||||
```go
|
||||
type Fooer interface {
|
||||
Foo() string
|
||||
}
|
||||
|
||||
type MyFooer string
|
||||
|
||||
func (b *MyFooer) Foo() string {
|
||||
return string(*b)
|
||||
}
|
||||
|
||||
func provideMyFooer() *MyFooer {
|
||||
b := new(MyFooer)
|
||||
*b = "Hello, World!"
|
||||
return b
|
||||
}
|
||||
|
||||
type Bar string
|
||||
|
||||
func provideBar(f Fooer) string {
|
||||
// f will be a *MyFooer.
|
||||
return f.Foo()
|
||||
}
|
||||
|
||||
var Set = wire.NewSet(
|
||||
provideMyFooer,
|
||||
wire.Bind(new(Fooer), new(*MyFooer)),
|
||||
provideBar)
|
||||
```
|
||||
|
||||
The first argument to `wire.Bind` is a pointer to a value of the desired
|
||||
interface type and the second argument is a pointer to a value of the type that
|
||||
implements the interface. Any set that includes an interface binding must also
|
||||
have a provider in the same set that provides the concrete type.
|
||||
|
||||
[type identity]: https://golang.org/ref/spec#Type_identity
|
||||
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
|
||||
|
||||
### Struct Providers
|
||||
|
||||
Structs can be constructed using provided types. Use the `wire.Struct` function
|
||||
to construct a struct type and tell the injector which field(s) should be injected.
|
||||
The injector will fill in each field using the provider for the field's type.
|
||||
For the resulting struct type `S`, `wire.Struct` provides both `S` and `*S`. For
|
||||
example, given the following providers:
|
||||
|
||||
```go
|
||||
type Foo int
|
||||
type Bar int
|
||||
|
||||
func ProvideFoo() Foo {/* ... */}
|
||||
|
||||
func ProvideBar() Bar {/* ... */}
|
||||
|
||||
type FooBar struct {
|
||||
MyFoo Foo
|
||||
MyBar Bar
|
||||
}
|
||||
|
||||
var Set = wire.NewSet(
|
||||
ProvideFoo,
|
||||
ProvideBar,
|
||||
wire.Struct(new(FooBar), "MyFoo", "MyBar"))
|
||||
```
|
||||
|
||||
A generated injector for `FooBar` would look like this:
|
||||
|
||||
```go
|
||||
func injectFooBar() FooBar {
|
||||
foo := ProvideFoo()
|
||||
bar := ProvideBar()
|
||||
fooBar := FooBar{
|
||||
MyFoo: foo,
|
||||
MyBar: bar,
|
||||
}
|
||||
return fooBar
|
||||
}
|
||||
```
|
||||
|
||||
The first argument to `wire.Struct` is a pointer to the desired struct type and
|
||||
the subsequent arguments are the names of fields to be injected. A special
|
||||
string `"*"` can be used as a shortcut to tell the injector to inject all
|
||||
fields. So `wire.Struct(new(FooBar), "*")` produces the same result as above.
|
||||
|
||||
For the above example, you can specify only injecting `"MyFoo"` by changing the
|
||||
`Set` to:
|
||||
|
||||
```go
|
||||
var Set = wire.NewSet(
|
||||
ProvideFoo,
|
||||
wire.Struct(new(FooBar), "MyFoo"))
|
||||
```
|
||||
|
||||
Then the generated injector for `FooBar` would look like this:
|
||||
|
||||
```go
|
||||
func injectFooBar() FooBar {
|
||||
foo := ProvideFoo()
|
||||
fooBar := FooBar{
|
||||
MyFoo: foo,
|
||||
}
|
||||
return fooBar
|
||||
}
|
||||
```
|
||||
|
||||
If the injector returned a `*FooBar` instead of a `FooBar`, the generated injector
|
||||
would look like this:
|
||||
|
||||
```go
|
||||
func injectFooBar() *FooBar {
|
||||
foo := ProvideFoo()
|
||||
fooBar := &FooBar{
|
||||
MyFoo: foo,
|
||||
}
|
||||
return fooBar
|
||||
}
|
||||
```
|
||||
|
||||
It is sometimes useful to prevent certain fields from being filled in by the
|
||||
injector, especially when passing `*` to `wire.Struct`. You can tag a field with
|
||||
`` `wire:"-"` `` to have Wire ignore such fields. For example:
|
||||
|
||||
```go
|
||||
type Foo struct {
|
||||
mu sync.Mutex `wire:"-"`
|
||||
Bar Bar
|
||||
}
|
||||
```
|
||||
|
||||
When you provide the `Foo` type using `wire.Struct(new(Foo), "*")`, Wire will
|
||||
automatically omit the `mu` field. Additionally, it is an error to explicitly
|
||||
specify a prevented field as in `wire.Struct(new(Foo), "mu")`.
|
||||
|
||||
### Binding Values
|
||||
|
||||
Occasionally, it is useful to bind a basic value (usually `nil`) to a type.
|
||||
Instead of having injectors depend on a throwaway provider function, you can add
|
||||
a value expression to a provider set.
|
||||
|
||||
```go
|
||||
type Foo struct {
|
||||
X int
|
||||
}
|
||||
|
||||
func injectFoo() Foo {
|
||||
wire.Build(wire.Value(Foo{X: 42}))
|
||||
return Foo{}
|
||||
}
|
||||
```
|
||||
|
||||
The generated injector would look like this:
|
||||
|
||||
```go
|
||||
func injectFoo() Foo {
|
||||
foo := _wireFooValue
|
||||
return foo
|
||||
}
|
||||
|
||||
var (
|
||||
_wireFooValue = Foo{X: 42}
|
||||
)
|
||||
```
|
||||
|
||||
It's important to note that the expression will be copied to the injector's
|
||||
package; references to variables will be evaluated during the injector package's
|
||||
initialization. Wire will emit an error if the expression calls any functions or
|
||||
receives from any channels.
|
||||
|
||||
For interface values, use `InterfaceValue`:
|
||||
|
||||
```go
|
||||
func injectReader() io.Reader {
|
||||
wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin))
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Use Fields of a Struct as Providers
|
||||
|
||||
Sometimes the providers the user wants are some fields of a struct. If you find
|
||||
yourself writing a provider like `getS` in the example below to promote struct
|
||||
fields into provided types:
|
||||
|
||||
```go
|
||||
type Foo struct {
|
||||
S string
|
||||
N int
|
||||
F float64
|
||||
}
|
||||
|
||||
func getS(foo Foo) string {
|
||||
// Bad! Use wire.FieldsOf instead.
|
||||
return foo.S
|
||||
}
|
||||
|
||||
func provideFoo() Foo {
|
||||
return Foo{ S: "Hello, World!", N: 1, F: 3.14 }
|
||||
}
|
||||
|
||||
func injectedMessage() string {
|
||||
wire.Build(
|
||||
provideFoo,
|
||||
getS)
|
||||
return ""
|
||||
}
|
||||
```
|
||||
|
||||
You can instead use `wire.FieldsOf` to use those fields directly without writing
|
||||
`getS`:
|
||||
|
||||
```go
|
||||
func injectedMessage() string {
|
||||
wire.Build(
|
||||
provideFoo,
|
||||
wire.FieldsOf(new(Foo), "S"))
|
||||
return ""
|
||||
}
|
||||
```
|
||||
|
||||
The generated injector would look like this:
|
||||
|
||||
```go
|
||||
func injectedMessage() string {
|
||||
foo := provideFoo()
|
||||
string2 := foo.S
|
||||
return string2
|
||||
}
|
||||
```
|
||||
|
||||
You can add as many field names to a `wire.FieldsOf` function as you like.
|
||||
For a given field type `T`, `FieldsOf` provides at least `T`; if the struct
|
||||
argument is a pointer to a struct, then `FieldsOf` also provides `*T`.
|
||||
|
||||
### Cleanup functions
|
||||
|
||||
If a provider creates a value that needs to be cleaned up (e.g. closing a file),
|
||||
then it can return a closure to clean up the resource. The injector will use
|
||||
this to either return an aggregated cleanup function to the caller or to clean
|
||||
up the resource if a provider called later in the injector's implementation
|
||||
returns an error.
|
||||
|
||||
```go
|
||||
func provideFile(log Logger, path Path) (*os.File, func(), error) {
|
||||
f, err := os.Open(string(path))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cleanup := func() {
|
||||
if err := f.Close(); err != nil {
|
||||
log.Log(err)
|
||||
}
|
||||
}
|
||||
return f, cleanup, nil
|
||||
}
|
||||
```
|
||||
|
||||
A cleanup function is guaranteed to be called before the cleanup function of any
|
||||
of the provider's inputs and must have the signature `func()`.
|
||||
|
||||
### Alternate Injector Syntax
|
||||
|
||||
If you grow weary of writing `return foobarbaz.Foo{}, nil` at the end of your
|
||||
injector function declaration, you can instead write it more concisely with a
|
||||
`panic`:
|
||||
|
||||
```go
|
||||
func injectFoo() Foo {
|
||||
panic(wire.Build(/* ... */))
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user