wire: FieldsOf now provides a pointer to the field type as well as the actual field type (#209)
This commit is contained in:
@@ -233,11 +233,11 @@ have a provider in the same set that provides the concrete type.
|
||||
|
||||
### Struct Providers
|
||||
|
||||
Structs can also be marked as providers. Use the `wire.Struct` function to
|
||||
inject a struct type and tell the injector which field(s) should be injected.
|
||||
Structs can be constructed using provided types. Use the `wire.Struct` function
|
||||
to construct a struct type and tell the injector which field(s) should be injected.
|
||||
The injector will fill in each field using the provider for the field's type.
|
||||
For a given struct type `S`, this would provide both `S` and `*S`. For example,
|
||||
given the following providers:
|
||||
For the resulting struct type `S`, `wire.Struct` provides both `S` and `*S`. For
|
||||
example, given the following providers:
|
||||
|
||||
```go
|
||||
type Foo int
|
||||
@@ -298,7 +298,18 @@ func injectFooBar() FooBar {
|
||||
}
|
||||
```
|
||||
|
||||
And similarly if the injector needed a `*FooBar`.
|
||||
If the injector returned a `*FooBar` instead of a `FooBar`, the generated injector
|
||||
would look like this:
|
||||
|
||||
```go
|
||||
func injectFooBar() *FooBar {
|
||||
foo := ProvideFoo()
|
||||
fooBar := &FooBar{
|
||||
MyFoo: foo,
|
||||
}
|
||||
return fooBar
|
||||
}
|
||||
```
|
||||
|
||||
It is sometimes useful to prevent certain fields from being filled in by the
|
||||
injector, especially when passing `*` to `wire.Struct`. You can tag a field with
|
||||
@@ -412,6 +423,7 @@ func injectedMessage() string {
|
||||
```
|
||||
|
||||
You can add as many field names to a `wire.FieldsOf` function as you like.
|
||||
For a given field type `T`, `FieldsOf` provides both `T` and `*T`.
|
||||
|
||||
### Cleanup functions
|
||||
|
||||
|
||||
@@ -84,6 +84,10 @@ type call struct {
|
||||
|
||||
valueExpr ast.Expr
|
||||
valueTypeInfo *types.Info
|
||||
|
||||
// The following are only set for kind == selectorExpr:
|
||||
|
||||
ptrToField bool
|
||||
}
|
||||
|
||||
// solve finds the sequence of calls required to produce an output type
|
||||
@@ -226,14 +230,18 @@ dfs:
|
||||
index.Set(curr.t, errAbort)
|
||||
continue dfs
|
||||
}
|
||||
// Use the args[0] to store the position of the parent struct.
|
||||
// Use args[0] to store the position of the parent struct.
|
||||
args := []int{v.(int)}
|
||||
// len(f.Out) is always 2; if curr.t is the 2nd one, then the call must
|
||||
// provide a pointer to the field.
|
||||
ptrToField := types.Identical(curr.t, f.Out[1])
|
||||
calls = append(calls, call{
|
||||
kind: selectorExpr,
|
||||
pkg: f.Pkg,
|
||||
name: f.Name,
|
||||
out: curr.t,
|
||||
args: args,
|
||||
kind: selectorExpr,
|
||||
pkg: f.Pkg,
|
||||
name: f.Name,
|
||||
out: curr.t,
|
||||
args: args,
|
||||
ptrToField: ptrToField,
|
||||
})
|
||||
default:
|
||||
panic("unknown return value from ProviderSet.For")
|
||||
@@ -382,12 +390,14 @@ func buildProviderMap(fset *token.FileSet, hasher typeutil.Hasher, set *Provider
|
||||
}
|
||||
for _, f := range set.Fields {
|
||||
src := &providerSetSrc{Field: f}
|
||||
if prevSrc := srcMap.At(f.Out); prevSrc != nil {
|
||||
ec.add(bindingConflictError(fset, f.Out, set, src, prevSrc.(*providerSetSrc)))
|
||||
continue
|
||||
for _, typ := range f.Out {
|
||||
if prevSrc := srcMap.At(typ); prevSrc != nil {
|
||||
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
|
||||
continue
|
||||
}
|
||||
providerMap.Set(typ, &ProvidedType{t: typ, f: f})
|
||||
srcMap.Set(typ, src)
|
||||
}
|
||||
providerMap.Set(f.Out, &ProvidedType{t: f.Out, f: f})
|
||||
srcMap.Set(f.Out, src)
|
||||
}
|
||||
if len(ec.errors) > 0 {
|
||||
return nil, nil, ec.errors
|
||||
|
||||
@@ -220,7 +220,7 @@ type InjectorArgs struct {
|
||||
Pos token.Pos
|
||||
}
|
||||
|
||||
// Field describes a list of fields from a struct.
|
||||
// Field describes a specific field selected from a struct.
|
||||
type Field struct {
|
||||
// Parent is the struct or pointer to the struct that the field belongs to.
|
||||
Parent types.Type
|
||||
@@ -231,8 +231,9 @@ type Field struct {
|
||||
// Pos is the source position of the field declaration.
|
||||
// defining these fields.
|
||||
Pos token.Pos
|
||||
// Out is the field's type.
|
||||
Out types.Type
|
||||
// Out is the field's provided types. The first element provides the
|
||||
// field type, the second element provides a pointer to it.
|
||||
Out []types.Type
|
||||
}
|
||||
|
||||
// Load finds all the provider sets in the packages that match the given
|
||||
@@ -458,7 +459,7 @@ func newObjectCache(pkgs []*packages.Package) *objectCache {
|
||||
}
|
||||
|
||||
// get converts a Go object into a Wire structure. It may return a *Provider, an
|
||||
// *IfaceBinding, a *ProviderSet, a *Value, or a *Fields.
|
||||
// *IfaceBinding, a *ProviderSet, a *Value, or a []*Field.
|
||||
func (oc *objectCache) get(obj types.Object) (val interface{}, errs []error) {
|
||||
ref := objRef{
|
||||
importPath: obj.Pkg().Path(),
|
||||
@@ -515,7 +516,7 @@ func (oc *objectCache) varDecl(obj *types.Var) *ast.ValueSpec {
|
||||
}
|
||||
|
||||
// processExpr converts an expression into a Wire structure. It may return a
|
||||
// *Provider, an *IfaceBinding, a *ProviderSet, a *Value or a *Field.
|
||||
// *Provider, an *IfaceBinding, a *ProviderSet, a *Value or a []*Field.
|
||||
func (oc *objectCache) processExpr(info *types.Info, pkgPath string, expr ast.Expr, varName string) (interface{}, []error) {
|
||||
exprPos := oc.fset.Position(expr.Pos())
|
||||
expr = astutil.Unparen(expr)
|
||||
@@ -988,7 +989,7 @@ func processInterfaceValue(fset *token.FileSet, info *types.Info, call *ast.Call
|
||||
}, nil
|
||||
}
|
||||
|
||||
// processFieldsOf creates a list of fields from a wire.FieldsOf call.
|
||||
// processFieldsOf creates a slice of fields from a wire.FieldsOf call.
|
||||
func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr) ([]*Field, error) {
|
||||
// Assumes that call.Fun is wire.FieldsOf.
|
||||
|
||||
@@ -1034,7 +1035,7 @@ func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr)
|
||||
Name: v.Name(),
|
||||
Pkg: v.Pkg(),
|
||||
Pos: v.Pos(),
|
||||
Out: v.Type(),
|
||||
Out: []types.Type{v.Type(), types.NewPointer(v.Type())},
|
||||
})
|
||||
}
|
||||
return fields, nil
|
||||
|
||||
@@ -26,4 +26,5 @@ func provideS() S {
|
||||
|
||||
func main() {
|
||||
fmt.Println(injectedMessage())
|
||||
fmt.Println("pointer to " + *injectedMessagePtr())
|
||||
}
|
||||
|
||||
@@ -26,3 +26,10 @@ func injectedMessage() string {
|
||||
wire.FieldsOf(new(S), "Foo"))
|
||||
return ""
|
||||
}
|
||||
|
||||
func injectedMessagePtr() *string {
|
||||
wire.Build(
|
||||
provideS,
|
||||
wire.FieldsOf(new(S), "Foo"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
Hello, World!
|
||||
pointer to Hello, World!
|
||||
|
||||
@@ -12,3 +12,9 @@ func injectedMessage() string {
|
||||
string2 := s.Foo
|
||||
return string2
|
||||
}
|
||||
|
||||
func injectedMessagePtr() *string {
|
||||
s := provideS()
|
||||
string2 := &s.Foo
|
||||
return string2
|
||||
}
|
||||
|
||||
@@ -731,10 +731,14 @@ func (ig *injectorGen) valueExpr(lname string, c *call) {
|
||||
|
||||
func (ig *injectorGen) fieldExpr(lname string, c *call) {
|
||||
a := c.args[0]
|
||||
ig.p("\t%s := ", lname)
|
||||
if c.ptrToField {
|
||||
ig.p("&")
|
||||
}
|
||||
if a < len(ig.paramNames) {
|
||||
ig.p("\t%s := %s.%s\n", lname, ig.paramNames[a], c.name)
|
||||
ig.p("%s.%s\n", ig.paramNames[a], c.name)
|
||||
} else {
|
||||
ig.p("\t%s := %s.%s\n", lname, ig.localNames[a-len(ig.paramNames)], c.name)
|
||||
ig.p("%s.%s\n", ig.localNames[a-len(ig.paramNames)], c.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
wire.go
3
wire.go
@@ -146,7 +146,8 @@ func InterfaceValue(typ interface{}, x interface{}) ProvidedValue {
|
||||
// A StructProvider represents a named struct.
|
||||
type StructProvider struct{}
|
||||
|
||||
// Struct specifies that the given struct type will be provided by filling in the fields in the struct that have the names given.
|
||||
// Struct specifies that the given struct type will be provided by filling in
|
||||
// the fields in the struct that have the names given.
|
||||
//
|
||||
// The first argument must be a pointer to the struct type. For a struct type
|
||||
// Foo, Wire will use field-filling to provide both Foo and *Foo. The remaining
|
||||
|
||||
Reference in New Issue
Block a user