wire: allow non-panic version of injector (google/go-cloud#91)

Fixes google/go-cloud#7
This commit is contained in:
Ross Light
2018-06-15 15:47:43 -07:00
parent 6345348d86
commit b2d47f8fcc
6 changed files with 107 additions and 29 deletions

View File

@@ -18,11 +18,13 @@ produce a value. These functions are ordinary Go code.
```go
package foobarbaz
type Foo int
type Foo struct {
X int
}
// ProvideFoo returns a Foo.
func ProvideFoo() Foo {
return 42
return Foo{X: 42}
}
```
@@ -33,11 +35,13 @@ package foobarbaz
// ...
type Bar int
type Bar struct {
X int
}
// ProvideBar returns a Bar: a negative Foo.
func ProvideBar(foo Foo) Bar {
return Bar(-foo)
return Bar{X: -foo.X}
}
```
@@ -53,14 +57,16 @@ import (
// ...
type Baz int
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 == 0 {
return 0, errors.New("cannot provide baz when bar is zero")
}
return Baz(bar), nil
return Baz{X: bar.X}, nil
}
```
@@ -102,9 +108,11 @@ 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 `panic()` with a call to `wire.Build` as its argument. 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`:
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
@@ -121,7 +129,8 @@ import (
)
func initializeApp(ctx context.Context) (foobarbaz.Baz, error) {
panic(wire.Build(foobarbaz.MegaSet))
wire.Build(foobarbaz.MegaSet)
return foobarbaz.Baz{}, nil
}
```
@@ -311,10 +320,13 @@ Instead of having injectors depend on a throwaway provider function, you can
add a value expression to a provider set.
```go
type Foo int
type Foo struct {
X int
}
func injectFoo() Foo {
panic(wire.Build(wire.Value(Foo(42))))
wire.Build(wire.Value(Foo{X: 42}))
return Foo{}
}
```
@@ -322,7 +334,7 @@ The generated injector would look like this:
```go
func injectFoo() Foo {
foo := Foo(42)
foo := Foo{X: 42}
return foo
}
```
@@ -355,3 +367,15 @@ func provideFile(log Logger, path Path) (*os.File, func(), error) {
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(/* ... */))
}
```

View File

@@ -578,6 +578,11 @@ func isInjector(info *types.Info, fn *ast.FuncDecl) *ast.CallExpr {
only = stmt
case *ast.EmptyStmt:
// Do nothing.
case *ast.ReturnStmt:
// Allow the function to end in a return.
if only == nil {
return nil
}
default:
return nil
}
@@ -585,29 +590,24 @@ func isInjector(info *types.Info, fn *ast.FuncDecl) *ast.CallExpr {
if only == nil {
return nil
}
panicCall, ok := only.X.(*ast.CallExpr)
call, ok := only.X.(*ast.CallExpr)
if !ok {
return nil
}
panicIdent, ok := panicCall.Fun.(*ast.Ident)
if qualifiedIdentObject(info, call.Fun) == types.Universe.Lookup("panic") {
if len(call.Args) != 1 {
return nil
}
call, ok = call.Args[0].(*ast.CallExpr)
if !ok {
return nil
}
if info.ObjectOf(panicIdent) != types.Universe.Lookup("panic") {
return nil
}
if len(panicCall.Args) != 1 {
return nil
}
buildCall, ok := panicCall.Args[0].(*ast.CallExpr)
if !ok {
return nil
}
buildObj := qualifiedIdentObject(info, buildCall.Fun)
buildObj := qualifiedIdentObject(info, call.Fun)
if !isWireImport(buildObj.Pkg().Path()) || buildObj.Name() != "Build" {
return nil
}
return buildCall
return call
}
func isWireImport(path string) bool {

View File

@@ -0,0 +1,26 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import "fmt"
func main() {
fmt.Println(injectedMessage())
}
// provideMessage provides a friendly user greeting.
func provideMessage() string {
return "Hello, World!"
}

View File

@@ -0,0 +1,26 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//+build wireinject
package main
import (
"github.com/google/go-cloud/wire"
)
func injectedMessage() string {
wire.Build(provideMessage)
return ""
}

View File

@@ -0,0 +1 @@
Hello, World!

View File

@@ -0,0 +1 @@
foo