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 ```go
package foobarbaz package foobarbaz
type Foo int type Foo struct {
X int
}
// ProvideFoo returns a Foo. // ProvideFoo returns a Foo.
func ProvideFoo() 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. // ProvideBar returns a Bar: a negative Foo.
func ProvideBar(foo Foo) Bar { 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. // 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 == 0 { if bar == 0 {
return 0, errors.New("cannot provide baz when bar is zero") 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. signature, then Wire generates the function's body.
An injector is declared by writing a function declaration whose body is a call 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 to `wire.Build`. The return values don't matter as long as they are of the
above providers were defined in a package called `example.com/foobarbaz`. The correct type. The values themselves will be ignored in the generated code. Let's
following would declare an injector to obtain a `Baz`: 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 ```go
// +build wireinject // +build wireinject
@@ -121,7 +129,8 @@ import (
) )
func initializeApp(ctx context.Context) (foobarbaz.Baz, error) { 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. add a value expression to a provider set.
```go ```go
type Foo int type Foo struct {
X int
}
func injectFoo() Foo { 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 ```go
func injectFoo() Foo { func injectFoo() Foo {
foo := Foo(42) foo := Foo{X: 42}
return foo 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 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()`. 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 only = stmt
case *ast.EmptyStmt: case *ast.EmptyStmt:
// Do nothing. // Do nothing.
case *ast.ReturnStmt:
// Allow the function to end in a return.
if only == nil {
return nil
}
default: default:
return nil return nil
} }
@@ -585,29 +590,24 @@ func isInjector(info *types.Info, fn *ast.FuncDecl) *ast.CallExpr {
if only == nil { if only == nil {
return nil return nil
} }
panicCall, ok := only.X.(*ast.CallExpr) call, ok := only.X.(*ast.CallExpr)
if !ok { if !ok {
return nil 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 { if !ok {
return nil return nil
} }
if info.ObjectOf(panicIdent) != types.Universe.Lookup("panic") {
return nil
} }
if len(panicCall.Args) != 1 { buildObj := qualifiedIdentObject(info, call.Fun)
return nil
}
buildCall, ok := panicCall.Args[0].(*ast.CallExpr)
if !ok {
return nil
}
buildObj := qualifiedIdentObject(info, buildCall.Fun)
if !isWireImport(buildObj.Pkg().Path()) || buildObj.Name() != "Build" { if !isWireImport(buildObj.Pkg().Path()) || buildObj.Name() != "Build" {
return nil return nil
} }
return buildCall return call
} }
func isWireImport(path string) bool { 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