From cacf1bc4ed0d7494548b767c0bc4f9d4b36c81a2 Mon Sep 17 00:00:00 2001 From: Ross Light Date: Thu, 29 Nov 2018 12:31:59 -0800 Subject: [PATCH] Split out documentation into multiple files (#85) Fixes #84 --- README.md | 615 +---------------------------------------- docs/best-practices.md | 121 ++++++++ docs/faq.md | 119 ++++++++ docs/guide.md | 369 +++++++++++++++++++++++++ wire.go | 2 +- 5 files changed, 625 insertions(+), 601 deletions(-) create mode 100644 docs/best-practices.md create mode 100644 docs/faq.md create mode 100644 docs/guide.md diff --git a/README.md b/README.md index ac8ab58..4c3597f 100644 --- a/README.md +++ b/README.md @@ -28,612 +28,27 @@ go get github.com/google/wire/cmd/wire 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 -value. These functions are ordinary Go code. +## Project status -```go -package foobarbaz +**This project is in alpha and is not yet suitable for production.** -type Foo struct { - X int -} +While in alpha, the API is subject to breaking changes. -// ProvideFoo returns a Foo. -func ProvideFoo() Foo { - return Foo{X: 42} -} -``` +## Community -Provider functions must be exported in order to be used from other packages, -just like ordinary functions. +You can contact us on the [go-cloud mailing list][]. -Providers can specify dependencies with parameters: +This project is covered by the Go [Code of Conduct][]. -```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(/* ... */)) -} -``` - -## 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. +[Code of Conduct]: ./CODE_OF_CONDUCT.md +[go-cloud mailing list]: https://groups.google.com/forum/#!forum/go-cloud diff --git a/docs/best-practices.md b/docs/best-practices.md new file mode 100644 index 0000000..fe401f5 --- /dev/null +++ b/docs/best-practices.md @@ -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. diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 0000000..f056b97 --- /dev/null +++ b/docs/faq.md @@ -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. diff --git a/docs/guide.md b/docs/guide.md new file mode 100644 index 0000000..35589d0 --- /dev/null +++ b/docs/guide.md @@ -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(/* ... */)) +} +``` diff --git a/wire.go b/wire.go index 541b46e..92a4ebe 100644 --- a/wire.go +++ b/wire.go @@ -14,7 +14,7 @@ // Package wire contains directives for Wire code generation. // 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 // tool. The entry point of Wire's analysis are injector functions: function