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
|
### Struct Providers
|
||||||
|
|
||||||
Structs can also be marked as providers. Use the `wire.Struct` function to
|
Structs can be constructed using provided types. Use the `wire.Struct` function
|
||||||
inject a struct type and tell the injector which field(s) should be injected.
|
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.
|
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,
|
For the resulting struct type `S`, `wire.Struct` provides both `S` and `*S`. For
|
||||||
given the following providers:
|
example, given the following providers:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Foo int
|
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
|
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
|
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.
|
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
|
### Cleanup functions
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ type call struct {
|
|||||||
|
|
||||||
valueExpr ast.Expr
|
valueExpr ast.Expr
|
||||||
valueTypeInfo *types.Info
|
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
|
// solve finds the sequence of calls required to produce an output type
|
||||||
@@ -226,14 +230,18 @@ dfs:
|
|||||||
index.Set(curr.t, errAbort)
|
index.Set(curr.t, errAbort)
|
||||||
continue dfs
|
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)}
|
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{
|
calls = append(calls, call{
|
||||||
kind: selectorExpr,
|
kind: selectorExpr,
|
||||||
pkg: f.Pkg,
|
pkg: f.Pkg,
|
||||||
name: f.Name,
|
name: f.Name,
|
||||||
out: curr.t,
|
out: curr.t,
|
||||||
args: args,
|
args: args,
|
||||||
|
ptrToField: ptrToField,
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
panic("unknown return value from ProviderSet.For")
|
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 {
|
for _, f := range set.Fields {
|
||||||
src := &providerSetSrc{Field: f}
|
src := &providerSetSrc{Field: f}
|
||||||
if prevSrc := srcMap.At(f.Out); prevSrc != nil {
|
for _, typ := range f.Out {
|
||||||
ec.add(bindingConflictError(fset, f.Out, set, src, prevSrc.(*providerSetSrc)))
|
if prevSrc := srcMap.At(typ); prevSrc != nil {
|
||||||
|
ec.add(bindingConflictError(fset, typ, set, src, prevSrc.(*providerSetSrc)))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
providerMap.Set(f.Out, &ProvidedType{t: f.Out, f: f})
|
providerMap.Set(typ, &ProvidedType{t: typ, f: f})
|
||||||
srcMap.Set(f.Out, src)
|
srcMap.Set(typ, src)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(ec.errors) > 0 {
|
if len(ec.errors) > 0 {
|
||||||
return nil, nil, ec.errors
|
return nil, nil, ec.errors
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ type InjectorArgs struct {
|
|||||||
Pos token.Pos
|
Pos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field describes a list of fields from a struct.
|
// Field describes a specific field selected from a struct.
|
||||||
type Field struct {
|
type Field struct {
|
||||||
// Parent is the struct or pointer to the struct that the field belongs to.
|
// Parent is the struct or pointer to the struct that the field belongs to.
|
||||||
Parent types.Type
|
Parent types.Type
|
||||||
@@ -231,8 +231,9 @@ type Field struct {
|
|||||||
// Pos is the source position of the field declaration.
|
// Pos is the source position of the field declaration.
|
||||||
// defining these fields.
|
// defining these fields.
|
||||||
Pos token.Pos
|
Pos token.Pos
|
||||||
// Out is the field's type.
|
// Out is the field's provided types. The first element provides the
|
||||||
Out types.Type
|
// 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
|
// 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
|
// 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) {
|
func (oc *objectCache) get(obj types.Object) (val interface{}, errs []error) {
|
||||||
ref := objRef{
|
ref := objRef{
|
||||||
importPath: obj.Pkg().Path(),
|
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
|
// 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) {
|
func (oc *objectCache) processExpr(info *types.Info, pkgPath string, expr ast.Expr, varName string) (interface{}, []error) {
|
||||||
exprPos := oc.fset.Position(expr.Pos())
|
exprPos := oc.fset.Position(expr.Pos())
|
||||||
expr = astutil.Unparen(expr)
|
expr = astutil.Unparen(expr)
|
||||||
@@ -988,7 +989,7 @@ func processInterfaceValue(fset *token.FileSet, info *types.Info, call *ast.Call
|
|||||||
}, nil
|
}, 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) {
|
func processFieldsOf(fset *token.FileSet, info *types.Info, call *ast.CallExpr) ([]*Field, error) {
|
||||||
// Assumes that call.Fun is wire.FieldsOf.
|
// 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(),
|
Name: v.Name(),
|
||||||
Pkg: v.Pkg(),
|
Pkg: v.Pkg(),
|
||||||
Pos: v.Pos(),
|
Pos: v.Pos(),
|
||||||
Out: v.Type(),
|
Out: []types.Type{v.Type(), types.NewPointer(v.Type())},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return fields, nil
|
return fields, nil
|
||||||
|
|||||||
@@ -26,4 +26,5 @@ func provideS() S {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println(injectedMessage())
|
fmt.Println(injectedMessage())
|
||||||
|
fmt.Println("pointer to " + *injectedMessagePtr())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,3 +26,10 @@ func injectedMessage() string {
|
|||||||
wire.FieldsOf(new(S), "Foo"))
|
wire.FieldsOf(new(S), "Foo"))
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func injectedMessagePtr() *string {
|
||||||
|
wire.Build(
|
||||||
|
provideS,
|
||||||
|
wire.FieldsOf(new(S), "Foo"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
Hello, World!
|
Hello, World!
|
||||||
|
pointer to Hello, World!
|
||||||
|
|||||||
@@ -12,3 +12,9 @@ func injectedMessage() string {
|
|||||||
string2 := s.Foo
|
string2 := s.Foo
|
||||||
return string2
|
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) {
|
func (ig *injectorGen) fieldExpr(lname string, c *call) {
|
||||||
a := c.args[0]
|
a := c.args[0]
|
||||||
|
ig.p("\t%s := ", lname)
|
||||||
|
if c.ptrToField {
|
||||||
|
ig.p("&")
|
||||||
|
}
|
||||||
if a < len(ig.paramNames) {
|
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 {
|
} 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.
|
// A StructProvider represents a named struct.
|
||||||
type StructProvider 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
|
// 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
|
// Foo, Wire will use field-filling to provide both Foo and *Foo. The remaining
|
||||||
|
|||||||
Reference in New Issue
Block a user