wire/doc: reformat README.md (google/go-cloud#495)
This commit is contained in:
committed by
Ross Light
parent
ec7cb36215
commit
32c3dc8578
225
README.md
225
README.md
@@ -23,19 +23,19 @@ Wire has two core concepts: providers and injectors.
|
|||||||
|
|
||||||
### Defining Providers
|
### Defining Providers
|
||||||
|
|
||||||
The primary mechanism in Wire is the **provider**: a function that can
|
The primary mechanism in Wire is the **provider**: a function that can produce a
|
||||||
produce a value. These functions are ordinary Go code.
|
value. These functions are ordinary Go code.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package foobarbaz
|
package foobarbaz
|
||||||
|
|
||||||
type Foo struct {
|
type Foo struct {
|
||||||
X int
|
X int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvideFoo returns a Foo.
|
// ProvideFoo returns a Foo.
|
||||||
func ProvideFoo() Foo {
|
func ProvideFoo() Foo {
|
||||||
return Foo{X: 42}
|
return Foo{X: 42}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -50,12 +50,12 @@ package foobarbaz
|
|||||||
// ...
|
// ...
|
||||||
|
|
||||||
type Bar struct {
|
type Bar struct {
|
||||||
X int
|
X int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvideBar returns a Bar: a negative Foo.
|
// ProvideBar returns a Bar: a negative Foo.
|
||||||
func ProvideBar(foo Foo) Bar {
|
func ProvideBar(foo Foo) Bar {
|
||||||
return Bar{X: -foo.X}
|
return Bar{X: -foo.X}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,22 +65,22 @@ Providers can also return errors:
|
|||||||
package foobarbaz
|
package foobarbaz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
type Baz struct {
|
type Baz struct {
|
||||||
X int
|
X int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProvideBaz returns a value if Bar is not zero.
|
// ProvideBaz returns a value if Bar is not zero.
|
||||||
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
|
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
|
||||||
if bar.X == 0 {
|
if bar.X == 0 {
|
||||||
return Baz{}, errors.New("cannot provide baz when bar is zero")
|
return Baz{}, errors.New("cannot provide baz when bar is zero")
|
||||||
}
|
}
|
||||||
return Baz{X: bar.X}, nil
|
return Baz{X: bar.X}, nil
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -92,8 +92,8 @@ called `SuperSet`, use the `wire.NewSet` function:
|
|||||||
package foobarbaz
|
package foobarbaz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// ...
|
// ...
|
||||||
"github.com/google/go-cloud/wire"
|
"github.com/google/go-cloud/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
@@ -107,8 +107,8 @@ You can also add other provider sets into a provider set.
|
|||||||
package foobarbaz
|
package foobarbaz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// ...
|
// ...
|
||||||
"example.com/some/other/pkg"
|
"example.com/some/other/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
@@ -136,22 +136,22 @@ say that the above providers were defined in a package called
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/google/go-cloud/wire"
|
"github.com/google/go-cloud/wire"
|
||||||
"example.com/foobarbaz"
|
"example.com/foobarbaz"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
||||||
wire.Build(foobarbaz.MegaSet)
|
wire.Build(foobarbaz.MegaSet)
|
||||||
return foobarbaz.Baz{}, nil
|
return foobarbaz.Baz{}, nil
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Like providers, injectors can be parameterized on inputs (which then get sent to
|
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
|
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
|
`wire.NewSet`: they form a provider set. This is the provider set that gets used
|
||||||
used during code generation for that injector.
|
during code generation for that injector.
|
||||||
|
|
||||||
Any non-injector declarations found in a file with injectors will be copied into
|
Any non-injector declarations found in a file with injectors will be copied into
|
||||||
the generated file.
|
the generated file.
|
||||||
@@ -174,17 +174,17 @@ Wire will produce an implementation of the injector in a file called
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"example.com/foobarbaz"
|
"example.com/foobarbaz"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
|
||||||
foo := foobarbaz.ProvideFoo()
|
foo := foobarbaz.ProvideFoo()
|
||||||
bar := foobarbaz.ProvideBar(foo)
|
bar := foobarbaz.ProvideBar(foo)
|
||||||
baz, err := foobarbaz.ProvideBaz(ctx, bar)
|
baz, err := foobarbaz.ProvideBaz(ctx, bar)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return baz, nil
|
return baz, nil
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -206,36 +206,36 @@ injectors.
|
|||||||
Frequently, dependency injection is used to bind a concrete implementation for
|
Frequently, dependency injection is used to bind a concrete implementation for
|
||||||
an interface. Wire matches inputs to outputs via [type identity][], so the
|
an interface. Wire matches inputs to outputs via [type identity][], so the
|
||||||
inclination might be to create a provider that returns an interface type.
|
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
|
However, this would not be idiomatic, since the Go best practice is to
|
||||||
concrete types][]. Instead, you can declare an interface binding in a provider
|
[return concrete types][]. Instead, you can declare an interface binding in a
|
||||||
set:
|
provider set:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Fooer interface {
|
type Fooer interface {
|
||||||
Foo() string
|
Foo() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Bar string
|
type Bar string
|
||||||
|
|
||||||
func (b *Bar) Foo() string {
|
func (b *Bar) Foo() string {
|
||||||
return string(*b)
|
return string(*b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideBar() *Bar {
|
func ProvideBar() *Bar {
|
||||||
b := new(Bar)
|
b := new(Bar)
|
||||||
*b = "Hello, World!"
|
*b = "Hello, World!"
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
var BarFooer = wire.NewSet(
|
var BarFooer = wire.NewSet(
|
||||||
ProvideBar,
|
ProvideBar,
|
||||||
wire.Bind(new(Fooer), new(Bar)))
|
wire.Bind(new(Fooer), new(Bar)))
|
||||||
```
|
```
|
||||||
|
|
||||||
The first argument to `wire.Bind` is a pointer to a value of the desired
|
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.
|
interface type and the second argument is a zero value of the concrete type. Any
|
||||||
Any set that includes an interface binding must also have a provider in the
|
set that includes an interface binding must also have a provider in the same set
|
||||||
same set that provides the concrete type.
|
that provides the concrete type.
|
||||||
|
|
||||||
[type identity]: https://golang.org/ref/spec#Type_identity
|
[type identity]: https://golang.org/ref/spec#Type_identity
|
||||||
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
|
[return concrete types]: https://github.com/golang/go/wiki/CodeReviewComments#interfaces
|
||||||
@@ -252,35 +252,35 @@ type Foo int
|
|||||||
type Bar int
|
type Bar int
|
||||||
|
|
||||||
func ProvideFoo() Foo {
|
func ProvideFoo() Foo {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideBar() Bar {
|
func ProvideBar() Bar {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
type FooBar struct {
|
type FooBar struct {
|
||||||
Foo Foo
|
Foo Foo
|
||||||
Bar Bar
|
Bar Bar
|
||||||
}
|
}
|
||||||
|
|
||||||
var Set = wire.NewSet(
|
var Set = wire.NewSet(
|
||||||
ProvideFoo,
|
ProvideFoo,
|
||||||
ProvideBar,
|
ProvideBar,
|
||||||
FooBar{})
|
FooBar{})
|
||||||
```
|
```
|
||||||
|
|
||||||
A generated injector for `FooBar` would look like this:
|
A generated injector for `FooBar` would look like this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func injectFooBar() FooBar {
|
func injectFooBar() FooBar {
|
||||||
foo := ProvideFoo()
|
foo := ProvideFoo()
|
||||||
bar := ProvideBar()
|
bar := ProvideBar()
|
||||||
fooBar := FooBar{
|
fooBar := FooBar{
|
||||||
Foo: foo,
|
Foo: foo,
|
||||||
Bar: bar,
|
Bar: bar,
|
||||||
}
|
}
|
||||||
return fooBar
|
return fooBar
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -289,17 +289,17 @@ And similarly if the injector needed a `*FooBar`.
|
|||||||
### Binding Values
|
### Binding Values
|
||||||
|
|
||||||
Occasionally, it is useful to bind a basic value (usually `nil`) to a type.
|
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
|
Instead of having injectors depend on a throwaway provider function, you can add
|
||||||
add a value expression to a provider set.
|
a value expression to a provider set.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Foo struct {
|
type Foo struct {
|
||||||
X int
|
X int
|
||||||
}
|
}
|
||||||
|
|
||||||
func injectFoo() Foo {
|
func injectFoo() Foo {
|
||||||
wire.Build(wire.Value(Foo{X: 42}))
|
wire.Build(wire.Value(Foo{X: 42}))
|
||||||
return Foo{}
|
return Foo{}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -307,24 +307,25 @@ The generated injector would look like this:
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func injectFoo() Foo {
|
func injectFoo() Foo {
|
||||||
foo := Foo{X: 42}
|
foo := Foo{X: 42}
|
||||||
return foo
|
return foo
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It's important to note that the expression will be copied to the injector's
|
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; references to variables will be evaluated during the injector package's
|
||||||
package's initialization. Wire will emit an error if the expression calls
|
initialization. Wire will emit an error if the expression calls any functions or
|
||||||
any functions or receives from any channels.
|
receives from any channels.
|
||||||
|
|
||||||
For interface values, use `InterfaceValue`:
|
For interface values, use `InterfaceValue`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func injectReader() io.Reader {
|
func injectReader() io.Reader {
|
||||||
wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin))
|
wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin))
|
||||||
return Foo{}
|
return Foo{}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cleanup functions
|
### Cleanup functions
|
||||||
|
|
||||||
If a provider creates a value that needs to be cleaned up (e.g. closing a file),
|
If a provider creates a value that needs to be cleaned up (e.g. closing a file),
|
||||||
@@ -335,16 +336,16 @@ returns an error.
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func provideFile(log Logger, path Path) (*os.File, func(), error) {
|
func provideFile(log Logger, path Path) (*os.File, func(), error) {
|
||||||
f, err := os.Open(string(path))
|
f, err := os.Open(string(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
log.Log(err)
|
log.Log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f, cleanup, nil
|
return f, cleanup, nil
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -359,7 +360,7 @@ injector function declaration, you can instead write it more concisely with a
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func injectFoo() Foo {
|
func injectFoo() Foo {
|
||||||
panic(wire.Build(/* ... */))
|
panic(wire.Build(/* ... */))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -370,8 +371,8 @@ over time.
|
|||||||
|
|
||||||
### Distinguishing Types
|
### Distinguishing Types
|
||||||
|
|
||||||
If you need to inject a common type like `string`, create a new string type
|
If you need to inject a common type like `string`, create a new string type to
|
||||||
to avoid conflicts with other providers. For example:
|
avoid conflicts with other providers. For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type MySQLConnectionString string
|
type MySQLConnectionString string
|
||||||
@@ -384,14 +385,14 @@ an options struct.
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// Messages is the set of recommended greetings.
|
// Messages is the set of recommended greetings.
|
||||||
Messages []Message
|
Messages []Message
|
||||||
// Writer is the location to send greetings. nil goes to stdout.
|
// Writer is the location to send greetings. nil goes to stdout.
|
||||||
Writer io.Writer
|
Writer io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
|
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
var GreeterSet = wire.NewSet(Options{}, NewGreeter)
|
var GreeterSet = wire.NewSet(Options{}, NewGreeter)
|
||||||
@@ -402,19 +403,19 @@ var GreeterSet = wire.NewSet(Options{}, NewGreeter)
|
|||||||
When creating a provider set for use in a library, the only changes you can make
|
When creating a provider set for use in a library, the only changes you can make
|
||||||
without breaking compatibility are:
|
without breaking compatibility are:
|
||||||
|
|
||||||
- Change which provider a provider set uses to provide a specific output, as
|
- 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
|
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
|
inputs. However, note that existing injectors will use the old provider
|
||||||
they are regenerated.
|
until they are regenerated.
|
||||||
- Introduce a new output type into the provider set, but only if the type itself
|
- Introduce a new output type into the provider set, but only if the type
|
||||||
is newly added. If the type is not new, it is possible that some injector
|
itself is newly added. If the type is not new, it is possible that some
|
||||||
already has the output type included, which would cause a conflict.
|
injector already has the output type included, which would cause a conflict.
|
||||||
|
|
||||||
All other changes are not safe. This includes:
|
All other changes are not safe. This includes:
|
||||||
|
|
||||||
- Requiring a new input in the provider set.
|
- Requiring a new input in the provider set.
|
||||||
- Removing an output type from a provider set.
|
- Removing an output type from a provider set.
|
||||||
- Adding an existing output type into the provider set.
|
- Adding an existing output type into the provider set.
|
||||||
|
|
||||||
Instead of making one of these breaking changes, consider adding a new provider
|
Instead of making one of these breaking changes, consider adding a new provider
|
||||||
set.
|
set.
|
||||||
@@ -425,30 +426,30 @@ As an example, if you have a provider set like this:
|
|||||||
var GreeterSet = wire.NewSet(NewStdoutGreeter)
|
var GreeterSet = wire.NewSet(NewStdoutGreeter)
|
||||||
|
|
||||||
func DefaultGreeter(ctx context.Context) *Greeter {
|
func DefaultGreeter(ctx context.Context) *Greeter {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
|
func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
|
func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You may:
|
You may:
|
||||||
|
|
||||||
- Use `DefaultGreeter` instead of `NewStdoutGreeter` in `GreeterSet`.
|
- Use `DefaultGreeter` instead of `NewStdoutGreeter` in `GreeterSet`.
|
||||||
- Create a new type `T` and add a provider for `T` to `GreeterSet`, as long as
|
- 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.
|
`T` is introduced in the same commit/release as the provider is added.
|
||||||
|
|
||||||
You may not:
|
You may not:
|
||||||
|
|
||||||
- Use `NewGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. This both
|
- Use `NewGreeter` instead of `NewStdoutGreeter` in `GreeterSet`. This both
|
||||||
adds an input type (`io.Writer`) and requires injectors to return an `error`
|
adds an input type (`io.Writer`) and requires injectors to return an `error`
|
||||||
where the provider of `*Greeter` did not require this before.
|
where the provider of `*Greeter` did not require this before.
|
||||||
- Remove `NewStdoutGreeter` from `GreeterSet`. Injectors depending on
|
- Remove `NewStdoutGreeter` from `GreeterSet`. Injectors depending on
|
||||||
`*Greeter` will be broken.
|
`*Greeter` will be broken.
|
||||||
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
|
- Add a provider for `io.Writer` to `GreeterSet`. Injectors might already have
|
||||||
a provider for `io.Writer` which might conflict with this one.
|
a provider for `io.Writer` which might conflict with this one.
|
||||||
|
|||||||
Reference in New Issue
Block a user