wire/doc: reformat README.md (google/go-cloud#495)

This commit is contained in:
Robert van Gent
2018-09-28 10:33:08 -07:00
committed by Ross Light
parent ec7cb36215
commit 32c3dc8578

225
README.md
View File

@@ -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.