615
README.md
615
README.md
@@ -28,612 +28,27 @@ go get github.com/google/wire/cmd/wire
|
|||||||
|
|
||||||
and ensuring that `$GOPATH/bin` is added to your `$PATH`.
|
and ensuring that `$GOPATH/bin` is added to your `$PATH`.
|
||||||
|
|
||||||
## Basics
|
## Documentation
|
||||||
|
|
||||||
Wire has two core concepts: providers and injectors.
|
- [User Guide][]
|
||||||
|
- [Best Practices][]
|
||||||
|
- [FAQ][]
|
||||||
|
|
||||||
### Defining Providers
|
[Best Practices]: ./docs/best-practices.md
|
||||||
|
[FAQ]: ./docs/faq.md
|
||||||
|
[User Guide]: ./docs/guide.md
|
||||||
|
|
||||||
The primary mechanism in Wire is the **provider**: a function that can produce a
|
## Project status
|
||||||
value. These functions are ordinary Go code.
|
|
||||||
|
|
||||||
```go
|
**This project is in alpha and is not yet suitable for production.**
|
||||||
package foobarbaz
|
|
||||||
|
|
||||||
type Foo struct {
|
While in alpha, the API is subject to breaking changes.
|
||||||
X int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProvideFoo returns a Foo.
|
## Community
|
||||||
func ProvideFoo() Foo {
|
|
||||||
return Foo{X: 42}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Provider functions must be exported in order to be used from other packages,
|
You can contact us on the [go-cloud mailing list][].
|
||||||
just like ordinary functions.
|
|
||||||
|
|
||||||
Providers can specify dependencies with parameters:
|
This project is covered by the Go [Code of Conduct][].
|
||||||
|
|
||||||
```go
|
[Code of Conduct]: ./CODE_OF_CONDUCT.md
|
||||||
package foobarbaz
|
[go-cloud mailing list]: https://groups.google.com/forum/#!forum/go-cloud
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
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 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 0, 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 Bar string
|
|
||||||
|
|
||||||
func (b *Bar) Foo() string {
|
|
||||||
return string(*b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideBar() *Bar {
|
|
||||||
b := new(Bar)
|
|
||||||
*b = "Hello, World!"
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
var BarFooer = wire.NewSet(
|
|
||||||
ProvideBar,
|
|
||||||
wire.Bind(new(Fooer), new(Bar)))
|
|
||||||
```
|
|
||||||
|
|
||||||
The first argument to `wire.Bind` is a pointer to a value of the desired
|
|
||||||
interface type and the second argument is a zero value of the concrete type. Any
|
|
||||||
set that includes an interface binding must also have a provider in the same set
|
|
||||||
that provides the concrete type.
|
|
||||||
|
|
||||||
If necessary, you can also bind one interface to another:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type FooerPlus interface {
|
|
||||||
Fooer
|
|
||||||
Bar() String
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideFooerPlus() FooerPlus {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
var FooerPlusAsFooer = wire.NewSet(
|
|
||||||
ProvideFooerPlus,
|
|
||||||
wire.Bind(new(Fooer), *new(FooerPlus)))
|
|
||||||
```
|
|
||||||
|
|
||||||
[type identity]: https://golang.org/ref/spec#Type_identity
|
|
||||||
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
|
|
||||||
|
|
||||||
### Struct Providers
|
|
||||||
|
|
||||||
Structs can also be marked as providers. Instead of calling a function, an
|
|
||||||
injector will fill in each field using the corresponding provider. For a given
|
|
||||||
struct type `S`, this would provide 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 {
|
|
||||||
Foo Foo
|
|
||||||
Bar Bar
|
|
||||||
}
|
|
||||||
|
|
||||||
var Set = wire.NewSet(
|
|
||||||
ProvideFoo,
|
|
||||||
ProvideBar,
|
|
||||||
FooBar{})
|
|
||||||
```
|
|
||||||
|
|
||||||
A generated injector for `FooBar` would look like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func injectFooBar() FooBar {
|
|
||||||
foo := ProvideFoo()
|
|
||||||
bar := ProvideBar()
|
|
||||||
fooBar := FooBar{
|
|
||||||
Foo: foo,
|
|
||||||
Bar: bar,
|
|
||||||
}
|
|
||||||
return fooBar
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And similarly if the injector needed a `*FooBar`.
|
|
||||||
|
|
||||||
### 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
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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(/* ... */))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
The following are practices we recommend for using Wire. This list will grow
|
|
||||||
over time.
|
|
||||||
|
|
||||||
### Distinguishing Types
|
|
||||||
|
|
||||||
If you need to inject a common type like `string`, create a new string type to
|
|
||||||
avoid conflicts with other providers. For example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MySQLConnectionString string
|
|
||||||
```
|
|
||||||
|
|
||||||
### Options Structs
|
|
||||||
|
|
||||||
A provider function that includes many dependencies can pair the function with
|
|
||||||
an options struct.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Options struct {
|
|
||||||
// Messages is the set of recommended greetings.
|
|
||||||
Messages []Message
|
|
||||||
// Writer is the location to send greetings. nil goes to stdout.
|
|
||||||
Writer io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
var GreeterSet = wire.NewSet(Options{}, NewGreeter)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Provider Sets in Libraries
|
|
||||||
|
|
||||||
When creating a provider set for use in a library, the only changes you can make
|
|
||||||
without breaking compatibility are:
|
|
||||||
|
|
||||||
- Change which provider a provider set uses to provide a specific output, as
|
|
||||||
long as it does not introduce a new input to the provider set. It may remove
|
|
||||||
inputs. However, note that existing injectors will use the old provider
|
|
||||||
until they are regenerated.
|
|
||||||
- Introduce a new output type into the provider set, but only if the type
|
|
||||||
itself is newly added. If the type is not new, it is possible that some
|
|
||||||
injector already has the output type included, which would cause a conflict.
|
|
||||||
|
|
||||||
All other changes are not safe. This includes:
|
|
||||||
|
|
||||||
- Requiring a new input in the provider set.
|
|
||||||
- Removing an output type from a provider set.
|
|
||||||
- Adding an existing output type into the provider set.
|
|
||||||
|
|
||||||
Instead of making one of these breaking changes, consider adding a new provider
|
|
||||||
set.
|
|
||||||
|
|
||||||
As an example, if you have a provider set like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
var GreeterSet = wire.NewSet(NewStdoutGreeter)
|
|
||||||
|
|
||||||
func DefaultGreeter(ctx context.Context) *Greeter {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You may:
|
|
||||||
|
|
||||||
- Use `DefaultGreeter` instead of `NewStdoutGreeter` in `GreeterSet`.
|
|
||||||
- Create a new type `T` and add a provider for `T` to `GreeterSet`, as long as
|
|
||||||
`T` is introduced in the same commit/release as the provider is added.
|
|
||||||
|
|
||||||
You may not:
|
|
||||||
|
|
||||||
- Use `NewGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. This both
|
|
||||||
adds an input type (`io.Writer`) and requires injectors to return an `error`
|
|
||||||
where the provider of `*Greeter` did not require this before.
|
|
||||||
- Remove `NewStdoutGreeter` from `GreeterSet`. Injectors depending on
|
|
||||||
`*Greeter` will be broken.
|
|
||||||
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
|
|
||||||
a provider for `io.Writer` which might conflict with this one.
|
|
||||||
|
|
||||||
As such, you should pick the output types in a library provider set carefully.
|
|
||||||
In general, prefer small provider sets in a library. For example, it is common
|
|
||||||
for a library provider set to contain a single provider function along with a
|
|
||||||
`wire.Bind` to the interface the return type implements. Avoiding larger
|
|
||||||
provider sets reduces the likelihood that applications will encounter conflicts.
|
|
||||||
To illustrate, imagine your library provides a client for a web service. While
|
|
||||||
it may be tempting to bundle a provider for `*http.Client` in a provider set for
|
|
||||||
your library's client, doing so would cause conflicts if every library did the
|
|
||||||
same. Instead, the library's provider set should only include the provider for
|
|
||||||
the API client, and let `*http.Client` be an input of the provider set.
|
|
||||||
|
|
||||||
### Mocking
|
|
||||||
|
|
||||||
There are two approaches for creating an injected app with mocked dependencies.
|
|
||||||
Examples of both approaches are shown
|
|
||||||
[here](https://github.com/google/wire/tree/master/internal/wire/testdata/ExampleWithMocks/foo).
|
|
||||||
|
|
||||||
#### Approach A: Pass mocks to the injector
|
|
||||||
|
|
||||||
Create a test-only injector that takes all of the mocks as arguments; the
|
|
||||||
argument types must be the interface types the mocks are mocking. `wire.Build`
|
|
||||||
can't include providers for the mocked dependencies without creating conflicts,
|
|
||||||
so if you're using provider set(s) you will need to define one that doesn't
|
|
||||||
include the mocked types.
|
|
||||||
|
|
||||||
#### Approach B: Return the mocks from the injector
|
|
||||||
|
|
||||||
Create a new struct that includes the app plus all of the dependencies you want
|
|
||||||
to mock. Create a test-only injector that returns this struct, give it providers
|
|
||||||
for the concrete mock types, and use `wire.Bind` to tell Wire that the
|
|
||||||
concrete mock types should be used to fulfill the appropriate interface.
|
|
||||||
|
|
||||||
## Frequently Asked Questions
|
|
||||||
|
|
||||||
### How does Wire relate to other Go dependency injection tools?
|
|
||||||
|
|
||||||
Other dependency injection tools for Go like [dig][] or [facebookgo/inject][]
|
|
||||||
are based on reflection. Wire runs as a code generator, which means that the
|
|
||||||
injector works without making calls to a runtime library. This enables easier
|
|
||||||
introspection of initialization and correct cross-references for tooling like
|
|
||||||
[guru][].
|
|
||||||
|
|
||||||
[dig]: https://github.com/uber-go/dig
|
|
||||||
[facebookgo/inject]: https://github.com/facebookgo/inject
|
|
||||||
[guru]: https://golang.org/s/using-guru
|
|
||||||
|
|
||||||
### How does Wire relate to other non-Go dependency injection tools (like Dagger 2)?
|
|
||||||
|
|
||||||
Wire's approach was inspired by [Dagger 2][]. However, it is not the aim of Wire
|
|
||||||
to emulate dependency injection tools from other languages: the design space and
|
|
||||||
requirements are quite different. For example, the Go compiler does not support
|
|
||||||
anything like Java's annotation processing mechanisms. The difference in
|
|
||||||
languages and their idioms necessarily requires different approaches in
|
|
||||||
primitives and API.
|
|
||||||
|
|
||||||
[Dagger 2]: https://google.github.io/dagger/
|
|
||||||
|
|
||||||
### Why use pseudo-functions to create provider sets or injectors?
|
|
||||||
|
|
||||||
In the early prototypes, Wire directives were specially formatted comments. This
|
|
||||||
seemed appealing at first glance as this meant no compile-time or runtime
|
|
||||||
impact. However, this unstructured approach becomes opaque to other tooling not
|
|
||||||
written for Wire. Tools like [`gorename`][] or [guru][] would not be able to
|
|
||||||
recognize references to the identifiers existing in comments without being
|
|
||||||
specially modified to understand Wire's comment format. By moving the references
|
|
||||||
into no-op function calls, Wire interoperates seamlessly with other Go tooling.
|
|
||||||
|
|
||||||
[`gorename`]: https://godoc.org/golang.org/x/tools/cmd/gorename
|
|
||||||
|
|
||||||
### What if my dependency graph has two dependencies of the same type?
|
|
||||||
|
|
||||||
This most frequently appears with common types like `string`. An example of this
|
|
||||||
problem would be:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Foo struct { /* ... */ }
|
|
||||||
type Bar struct { /* ... */ }
|
|
||||||
|
|
||||||
func newFoo1() *Foo { /* ... */ }
|
|
||||||
func newFoo2() *Foo { /* ... */ }
|
|
||||||
func newBar(foo1 *Foo, foo2 *Foo) *Bar { /* ... */ }
|
|
||||||
|
|
||||||
func inject() *Bar {
|
|
||||||
// ERROR! Multiple providers for *Foo.
|
|
||||||
wire.Build(newFoo1, newFoo2, newBar)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Wire does not allow multiple providers for one type to exist in the transitive
|
|
||||||
closure of the providers presented to `wire.Build`, as this is usually a
|
|
||||||
mistake. For legitimate cases where you need multiple dependencies of the same
|
|
||||||
type, you need to invent a new type to call this other dependency. For example,
|
|
||||||
you can name OAuth credentials after the service they connect to. Once you have
|
|
||||||
a suitable different type, you can wrap and unwrap the type when plumbing it
|
|
||||||
through Wire. Continuing our above example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type OtherFoo Foo
|
|
||||||
|
|
||||||
func newOtherFoo() *OtherFoo {
|
|
||||||
// Call the original provider...
|
|
||||||
foo := newFoo2()
|
|
||||||
// ...then convert it to the new type.
|
|
||||||
return (*OtherFoo)(foo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func provideBar(foo1 *Foo, otherFoo *OtherFoo) *Bar {
|
|
||||||
// Convert the new type into the unwrapped type...
|
|
||||||
foo2 := (*Foo)(otherFoo)
|
|
||||||
// ...then use it to call the original provider.
|
|
||||||
return newBar(foo1, foo2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func inject() *Bar {
|
|
||||||
wire.Build(newFoo1, newOtherFoo, provideBar)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why does Wire forbid including the same provider multiple times?
|
|
||||||
|
|
||||||
Wire forbids this to remain consistent with the principle that specifying
|
|
||||||
multiple providers for the same type is an error. On the surface, Wire could
|
|
||||||
permit duplication, but this would introduce a few unintended consequences:
|
|
||||||
|
|
||||||
- Wire would have to specify what kinds of duplicates are permissible: are two
|
|
||||||
`wire.Value` calls ever considered to be the "same"?
|
|
||||||
- If a provider set changes the function it uses to provide a type, then this
|
|
||||||
could break an application, since it may introduce a new conflict between
|
|
||||||
another provider set that was specifying the "same" provider.
|
|
||||||
|
|
||||||
As such, we decided that the simpler behavior would be for this case to be an
|
|
||||||
error, knowing we can always relax this restriction later. The user can always
|
|
||||||
create a new provider set that does not have the conflicting type. A [proposed
|
|
||||||
subtract command][] would automate the toil in this process.
|
|
||||||
|
|
||||||
[proposed subtract command]: https://github.com/google/go-cloud/issues/51
|
|
||||||
|
|
||||||
### Should I use Wire for small applications?
|
|
||||||
|
|
||||||
Probably not. Wire is designed to automate more intricate setup code found in
|
|
||||||
larger applications. For small applications, hand-wiring dependencies is
|
|
||||||
simpler.
|
|
||||||
|
|
||||||
### Who is using Wire?
|
|
||||||
|
|
||||||
Wire is still fairly new and doesn't have a large user base yet. However, we
|
|
||||||
have heard a lot of interest from Go users wanting to simplify their
|
|
||||||
applications. If your project or company uses Wire, please let us know by either
|
|
||||||
emailing us or sending a pull request amending this section.
|
|
||||||
|
|||||||
121
docs/best-practices.md
Normal file
121
docs/best-practices.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Best Practices
|
||||||
|
|
||||||
|
The following are practices we recommend for using Wire. This list will grow
|
||||||
|
over time.
|
||||||
|
|
||||||
|
## Distinguishing Types
|
||||||
|
|
||||||
|
If you need to inject a common type like `string`, create a new string type to
|
||||||
|
avoid conflicts with other providers. For example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MySQLConnectionString string
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options Structs
|
||||||
|
|
||||||
|
A provider function that includes many dependencies can pair the function with
|
||||||
|
an options struct.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Options struct {
|
||||||
|
// Messages is the set of recommended greetings.
|
||||||
|
Messages []Message
|
||||||
|
// Writer is the location to send greetings. nil goes to stdout.
|
||||||
|
Writer io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
var GreeterSet = wire.NewSet(Options{}, NewGreeter)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Provider Sets in Libraries
|
||||||
|
|
||||||
|
When creating a provider set for use in a library, the only changes you can make
|
||||||
|
without breaking compatibility are:
|
||||||
|
|
||||||
|
- Change which provider a provider set uses to provide a specific output, as
|
||||||
|
long as it does not introduce a new input to the provider set. It may remove
|
||||||
|
inputs. However, note that existing injectors will use the old provider
|
||||||
|
until they are regenerated.
|
||||||
|
- Introduce a new output type into the provider set, but only if the type
|
||||||
|
itself is newly added. If the type is not new, it is possible that some
|
||||||
|
injector already has the output type included, which would cause a conflict.
|
||||||
|
|
||||||
|
All other changes are not safe. This includes:
|
||||||
|
|
||||||
|
- Requiring a new input in the provider set.
|
||||||
|
- Removing an output type from a provider set.
|
||||||
|
- Adding an existing output type into the provider set.
|
||||||
|
|
||||||
|
Instead of making one of these breaking changes, consider adding a new provider
|
||||||
|
set.
|
||||||
|
|
||||||
|
As an example, if you have a provider set like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var GreeterSet = wire.NewSet(NewStdoutGreeter)
|
||||||
|
|
||||||
|
func DefaultGreeter(ctx context.Context) *Greeter {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- Use `DefaultGreeter` instead of `NewStdoutGreeter` in `GreeterSet`.
|
||||||
|
- Create a new type `T` and add a provider for `T` to `GreeterSet`, as long as
|
||||||
|
`T` is introduced in the same commit/release as the provider is added.
|
||||||
|
|
||||||
|
You may not:
|
||||||
|
|
||||||
|
- Use `NewGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. This both
|
||||||
|
adds an input type (`io.Writer`) and requires injectors to return an `error`
|
||||||
|
where the provider of `*Greeter` did not require this before.
|
||||||
|
- Remove `NewStdoutGreeter` from `GreeterSet`. Injectors depending on
|
||||||
|
`*Greeter` will be broken.
|
||||||
|
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
|
||||||
|
a provider for `io.Writer` which might conflict with this one.
|
||||||
|
|
||||||
|
As such, you should pick the output types in a library provider set carefully.
|
||||||
|
In general, prefer small provider sets in a library. For example, it is common
|
||||||
|
for a library provider set to contain a single provider function along with a
|
||||||
|
`wire.Bind` to the interface the return type implements. Avoiding larger
|
||||||
|
provider sets reduces the likelihood that applications will encounter conflicts.
|
||||||
|
To illustrate, imagine your library provides a client for a web service. While
|
||||||
|
it may be tempting to bundle a provider for `*http.Client` in a provider set for
|
||||||
|
your library's client, doing so would cause conflicts if every library did the
|
||||||
|
same. Instead, the library's provider set should only include the provider for
|
||||||
|
the API client, and let `*http.Client` be an input of the provider set.
|
||||||
|
|
||||||
|
## Mocking
|
||||||
|
|
||||||
|
There are two approaches for creating an injected app with mocked dependencies.
|
||||||
|
Examples of both approaches are shown
|
||||||
|
[here](https://github.com/google/wire/tree/master/internal/wire/testdata/ExampleWithMocks/foo).
|
||||||
|
|
||||||
|
### Approach A: Pass mocks to the injector
|
||||||
|
|
||||||
|
Create a test-only injector that takes all of the mocks as arguments; the
|
||||||
|
argument types must be the interface types the mocks are mocking. `wire.Build`
|
||||||
|
can't include providers for the mocked dependencies without creating conflicts,
|
||||||
|
so if you're using provider set(s) you will need to define one that doesn't
|
||||||
|
include the mocked types.
|
||||||
|
|
||||||
|
### Approach B: Return the mocks from the injector
|
||||||
|
|
||||||
|
Create a new struct that includes the app plus all of the dependencies you want
|
||||||
|
to mock. Create a test-only injector that returns this struct, give it providers
|
||||||
|
for the concrete mock types, and use `wire.Bind` to tell Wire that the
|
||||||
|
concrete mock types should be used to fulfill the appropriate interface.
|
||||||
119
docs/faq.md
Normal file
119
docs/faq.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
## How does Wire relate to other Go dependency injection tools?
|
||||||
|
|
||||||
|
Other dependency injection tools for Go like [dig][] or [facebookgo/inject][]
|
||||||
|
are based on reflection. Wire runs as a code generator, which means that the
|
||||||
|
injector works without making calls to a runtime library. This enables easier
|
||||||
|
introspection of initialization and correct cross-references for tooling like
|
||||||
|
[guru][].
|
||||||
|
|
||||||
|
[dig]: https://github.com/uber-go/dig
|
||||||
|
[facebookgo/inject]: https://github.com/facebookgo/inject
|
||||||
|
[guru]: https://golang.org/s/using-guru
|
||||||
|
|
||||||
|
## How does Wire relate to other non-Go dependency injection tools (like Dagger 2)?
|
||||||
|
|
||||||
|
Wire's approach was inspired by [Dagger 2][]. However, it is not the aim of Wire
|
||||||
|
to emulate dependency injection tools from other languages: the design space and
|
||||||
|
requirements are quite different. For example, the Go compiler does not support
|
||||||
|
anything like Java's annotation processing mechanisms. The difference in
|
||||||
|
languages and their idioms necessarily requires different approaches in
|
||||||
|
primitives and API.
|
||||||
|
|
||||||
|
[Dagger 2]: https://google.github.io/dagger/
|
||||||
|
|
||||||
|
## Why use pseudo-functions to create provider sets or injectors?
|
||||||
|
|
||||||
|
In the early prototypes, Wire directives were specially formatted comments. This
|
||||||
|
seemed appealing at first glance as this meant no compile-time or runtime
|
||||||
|
impact. However, this unstructured approach becomes opaque to other tooling not
|
||||||
|
written for Wire. Tools like [`gorename`][] or [guru][] would not be able to
|
||||||
|
recognize references to the identifiers existing in comments without being
|
||||||
|
specially modified to understand Wire's comment format. By moving the references
|
||||||
|
into no-op function calls, Wire interoperates seamlessly with other Go tooling.
|
||||||
|
|
||||||
|
[`gorename`]: https://godoc.org/golang.org/x/tools/cmd/gorename
|
||||||
|
|
||||||
|
## What if my dependency graph has two dependencies of the same type?
|
||||||
|
|
||||||
|
This most frequently appears with common types like `string`. An example of this
|
||||||
|
problem would be:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Foo struct { /* ... */ }
|
||||||
|
type Bar struct { /* ... */ }
|
||||||
|
|
||||||
|
func newFoo1() *Foo { /* ... */ }
|
||||||
|
func newFoo2() *Foo { /* ... */ }
|
||||||
|
func newBar(foo1 *Foo, foo2 *Foo) *Bar { /* ... */ }
|
||||||
|
|
||||||
|
func inject() *Bar {
|
||||||
|
// ERROR! Multiple providers for *Foo.
|
||||||
|
wire.Build(newFoo1, newFoo2, newBar)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Wire does not allow multiple providers for one type to exist in the transitive
|
||||||
|
closure of the providers presented to `wire.Build`, as this is usually a
|
||||||
|
mistake. For legitimate cases where you need multiple dependencies of the same
|
||||||
|
type, you need to invent a new type to call this other dependency. For example,
|
||||||
|
you can name OAuth credentials after the service they connect to. Once you have
|
||||||
|
a suitable different type, you can wrap and unwrap the type when plumbing it
|
||||||
|
through Wire. Continuing our above example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type OtherFoo Foo
|
||||||
|
|
||||||
|
func newOtherFoo() *OtherFoo {
|
||||||
|
// Call the original provider...
|
||||||
|
foo := newFoo2()
|
||||||
|
// ...then convert it to the new type.
|
||||||
|
return (*OtherFoo)(foo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func provideBar(foo1 *Foo, otherFoo *OtherFoo) *Bar {
|
||||||
|
// Convert the new type into the unwrapped type...
|
||||||
|
foo2 := (*Foo)(otherFoo)
|
||||||
|
// ...then use it to call the original provider.
|
||||||
|
return newBar(foo1, foo2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inject() *Bar {
|
||||||
|
wire.Build(newFoo1, newOtherFoo, provideBar)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why does Wire forbid including the same provider multiple times?
|
||||||
|
|
||||||
|
Wire forbids this to remain consistent with the principle that specifying
|
||||||
|
multiple providers for the same type is an error. On the surface, Wire could
|
||||||
|
permit duplication, but this would introduce a few unintended consequences:
|
||||||
|
|
||||||
|
- Wire would have to specify what kinds of duplicates are permissible: are two
|
||||||
|
`wire.Value` calls ever considered to be the "same"?
|
||||||
|
- If a provider set changes the function it uses to provide a type, then this
|
||||||
|
could break an application, since it may introduce a new conflict between
|
||||||
|
another provider set that was specifying the "same" provider.
|
||||||
|
|
||||||
|
As such, we decided that the simpler behavior would be for this case to be an
|
||||||
|
error, knowing we can always relax this restriction later. The user can always
|
||||||
|
create a new provider set that does not have the conflicting type. A [proposed
|
||||||
|
subtract command][] would automate the toil in this process.
|
||||||
|
|
||||||
|
[proposed subtract command]: https://github.com/google/wire/issues/8
|
||||||
|
|
||||||
|
## Should I use Wire for small applications?
|
||||||
|
|
||||||
|
Probably not. Wire is designed to automate more intricate setup code found in
|
||||||
|
larger applications. For small applications, hand-wiring dependencies is
|
||||||
|
simpler.
|
||||||
|
|
||||||
|
## Who is using Wire?
|
||||||
|
|
||||||
|
Wire is still fairly new and doesn't have a large user base yet. However, we
|
||||||
|
have heard a lot of interest from Go users wanting to simplify their
|
||||||
|
applications. If your project or company uses Wire, please let us know by either
|
||||||
|
emailing us or sending a pull request amending this section.
|
||||||
369
docs/guide.md
Normal file
369
docs/guide.md
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
# 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 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 0, 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 Bar string
|
||||||
|
|
||||||
|
func (b *Bar) Foo() string {
|
||||||
|
return string(*b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideBar() *Bar {
|
||||||
|
b := new(Bar)
|
||||||
|
*b = "Hello, World!"
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
var BarFooer = wire.NewSet(
|
||||||
|
ProvideBar,
|
||||||
|
wire.Bind(new(Fooer), new(Bar)))
|
||||||
|
```
|
||||||
|
|
||||||
|
The first argument to `wire.Bind` is a pointer to a value of the desired
|
||||||
|
interface type and the second argument is a zero value of the concrete type. Any
|
||||||
|
set that includes an interface binding must also have a provider in the same set
|
||||||
|
that provides the concrete type.
|
||||||
|
|
||||||
|
If necessary, you can also bind one interface to another:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type FooerPlus interface {
|
||||||
|
Fooer
|
||||||
|
Bar() String
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideFooerPlus() FooerPlus {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
var FooerPlusAsFooer = wire.NewSet(
|
||||||
|
ProvideFooerPlus,
|
||||||
|
wire.Bind(new(Fooer), *new(FooerPlus)))
|
||||||
|
```
|
||||||
|
|
||||||
|
[type identity]: https://golang.org/ref/spec#Type_identity
|
||||||
|
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
|
||||||
|
|
||||||
|
### Struct Providers
|
||||||
|
|
||||||
|
Structs can also be marked as providers. Instead of calling a function, an
|
||||||
|
injector will fill in each field using the corresponding provider. For a given
|
||||||
|
struct type `S`, this would provide 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 {
|
||||||
|
Foo Foo
|
||||||
|
Bar Bar
|
||||||
|
}
|
||||||
|
|
||||||
|
var Set = wire.NewSet(
|
||||||
|
ProvideFoo,
|
||||||
|
ProvideBar,
|
||||||
|
FooBar{})
|
||||||
|
```
|
||||||
|
|
||||||
|
A generated injector for `FooBar` would look like this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func injectFooBar() FooBar {
|
||||||
|
foo := ProvideFoo()
|
||||||
|
bar := ProvideBar()
|
||||||
|
fooBar := FooBar{
|
||||||
|
Foo: foo,
|
||||||
|
Bar: bar,
|
||||||
|
}
|
||||||
|
return fooBar
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And similarly if the injector needed a `*FooBar`.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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(/* ... */))
|
||||||
|
}
|
||||||
|
```
|
||||||
2
wire.go
2
wire.go
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
// Package wire contains directives for Wire code generation.
|
// Package wire contains directives for Wire code generation.
|
||||||
// For an overview of working with Wire, see the user guide at
|
// For an overview of working with Wire, see the user guide at
|
||||||
// https://github.com/google/wire/blob/master/README.md
|
// https://github.com/google/wire/blob/master/docs/guide.md
|
||||||
//
|
//
|
||||||
// The directives in this package are used as input to the Wire code generation
|
// The directives in this package are used as input to the Wire code generation
|
||||||
// tool. The entry point of Wire's analysis are injector functions: function
|
// tool. The entry point of Wire's analysis are injector functions: function
|
||||||
|
|||||||
Reference in New Issue
Block a user