|
@@ -0,0 +1,576 @@
|
|
|
|
|
+// Package inject provides a reflect based injector. A large application built
|
|
|
|
|
+// with dependency injection in mind will typically involve the boring work of
|
|
|
|
|
+// setting up the object graph. This library attempts to take care of this
|
|
|
|
|
+// boring work by creating and connecting the various objects. Its use involves
|
|
|
|
|
+// you seeding the object graph with some (possibly incomplete) objects, where
|
|
|
|
|
+// the underlying types have been tagged for injection. Given this, the
|
|
|
|
|
+// library will populate the objects creating new ones as necessary. It uses
|
|
|
|
|
+// singletons by default, supports optional private instances as well as named
|
|
|
|
|
+// instances.
|
|
|
|
|
+//
|
|
|
|
|
+// It works using Go's reflection package and is inherently limited in what it
|
|
|
|
|
+// can do as opposed to a code-gen system with respect to private fields.
|
|
|
|
|
+//
|
|
|
|
|
+// The usage pattern for the library involves struct tags. It requires the tag
|
|
|
|
|
+// format used by the various standard libraries, like json, xml etc. It
|
|
|
|
|
+// involves tags in one of the three forms below:
|
|
|
|
|
+//
|
|
|
|
|
+// `inject:""`
|
|
|
|
|
+// `inject:"private"`
|
|
|
|
|
+// `inject:"dev logger"`
|
|
|
|
|
+//
|
|
|
|
|
+// The first no value syntax is for the common case of a singleton dependency
|
|
|
|
|
+// of the associated type. The second triggers creation of a private instance
|
|
|
|
|
+// for the associated type. Finally the last form is asking for a named
|
|
|
|
|
+// dependency called "dev logger".
|
|
|
|
|
+package inject
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "bytes"
|
|
|
|
|
+ "fmt"
|
|
|
|
|
+ "math/rand"
|
|
|
|
|
+ "reflect"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/facebookgo/structtag"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// Logger allows for simple logging as inject traverses and populates the
|
|
|
|
|
+// object graph.
|
|
|
|
|
+type Logger interface {
|
|
|
|
|
+ Debugf(format string, v ...interface{})
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Populate is a short-hand for populating a graph with the given incomplete
|
|
|
|
|
+// object values.
|
|
|
|
|
+func Populate(values ...interface{}) error {
|
|
|
|
|
+ var g Graph
|
|
|
|
|
+ for _, v := range values {
|
|
|
|
|
+ if err := g.Provide(&Object{Value: v}); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return g.Populate()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// An Object in the Graph.
|
|
|
|
|
+type Object struct {
|
|
|
|
|
+ Value interface{}
|
|
|
|
|
+ Name string // Optional
|
|
|
|
|
+ Complete bool // If true, the Value will be considered complete
|
|
|
|
|
+ Fields map[string]*Object // Populated with the field names that were injected and their corresponding *Object.
|
|
|
|
|
+ reflectType reflect.Type
|
|
|
|
|
+ reflectValue reflect.Value
|
|
|
|
|
+ private bool // If true, the Value will not be used and will only be populated
|
|
|
|
|
+ created bool // If true, the Object was created by us
|
|
|
|
|
+ embedded bool // If true, the Object is an embedded struct provided internally
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// String representation suitable for human consumption.
|
|
|
|
|
+func (o *Object) String() string {
|
|
|
|
|
+ var buf bytes.Buffer
|
|
|
|
|
+ fmt.Fprint(&buf, o.reflectType)
|
|
|
|
|
+ if o.Name != "" {
|
|
|
|
|
+ fmt.Fprintf(&buf, " named %s", o.Name)
|
|
|
|
|
+ }
|
|
|
|
|
+ return buf.String()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (o *Object) addDep(field string, dep *Object) {
|
|
|
|
|
+ if o.Fields == nil {
|
|
|
|
|
+ o.Fields = make(map[string]*Object)
|
|
|
|
|
+ }
|
|
|
|
|
+ o.Fields[field] = dep
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// The Graph of Objects.
|
|
|
|
|
+type Graph struct {
|
|
|
|
|
+ Logger Logger // Optional, will trigger debug logging.
|
|
|
|
|
+ unnamed []*Object
|
|
|
|
|
+ unnamedType map[reflect.Type]bool
|
|
|
|
|
+ named map[string]*Object
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Provide objects to the Graph. The Object documentation describes
|
|
|
|
|
+// the impact of various fields.
|
|
|
|
|
+func (g *Graph) Provide(objects ...*Object) error {
|
|
|
|
|
+ for _, o := range objects {
|
|
|
|
|
+ o.reflectType = reflect.TypeOf(o.Value)
|
|
|
|
|
+ o.reflectValue = reflect.ValueOf(o.Value)
|
|
|
|
|
+
|
|
|
|
|
+ if o.Fields != nil {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "fields were specified on object %s when it was provided",
|
|
|
|
|
+ o,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if o.Name == "" {
|
|
|
|
|
+ if !isStructPtr(o.reflectType) {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "expected unnamed object value to be a pointer to a struct but got type %s "+
|
|
|
|
|
+ "with value %v",
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ o.Value,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if !o.private {
|
|
|
|
|
+ if g.unnamedType == nil {
|
|
|
|
|
+ g.unnamedType = make(map[reflect.Type]bool)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if g.unnamedType[o.reflectType] {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "provided two unnamed instances of type *%s.%s",
|
|
|
|
|
+ o.reflectType.Elem().PkgPath(), o.reflectType.Elem().Name(),
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ g.unnamedType[o.reflectType] = true
|
|
|
|
|
+ }
|
|
|
|
|
+ g.unnamed = append(g.unnamed, o)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if g.named == nil {
|
|
|
|
|
+ g.named = make(map[string]*Object)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if g.named[o.Name] != nil {
|
|
|
|
|
+ return fmt.Errorf("provided two instances named %s", o.Name)
|
|
|
|
|
+ }
|
|
|
|
|
+ g.named[o.Name] = o
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if g.Logger != nil {
|
|
|
|
|
+ if o.created {
|
|
|
|
|
+ g.Logger.Debugf("created %s", o)
|
|
|
|
|
+ } else if o.embedded {
|
|
|
|
|
+ g.Logger.Debugf("provided embedded %s", o)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ g.Logger.Debugf("provided %s", o)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Populate the incomplete Objects.
|
|
|
|
|
+func (g *Graph) Populate() error {
|
|
|
|
|
+ for _, o := range g.named {
|
|
|
|
|
+ if o.Complete {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := g.populateExplicit(o); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // We append and modify our slice as we go along, so we don't use a standard
|
|
|
|
|
+ // range loop, and do a single pass thru each object in our graph.
|
|
|
|
|
+ i := 0
|
|
|
|
|
+ for {
|
|
|
|
|
+ if i == len(g.unnamed) {
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ o := g.unnamed[i]
|
|
|
|
|
+ i++
|
|
|
|
|
+
|
|
|
|
|
+ if o.Complete {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := g.populateExplicit(o); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // A Second pass handles injecting Interface values to ensure we have created
|
|
|
|
|
+ // all concrete types first.
|
|
|
|
|
+ for _, o := range g.unnamed {
|
|
|
|
|
+ if o.Complete {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := g.populateUnnamedInterface(o); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for _, o := range g.named {
|
|
|
|
|
+ if o.Complete {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if err := g.populateUnnamedInterface(o); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (g *Graph) populateExplicit(o *Object) error {
|
|
|
|
|
+ // Ignore named value types.
|
|
|
|
|
+ if o.Name != "" && !isStructPtr(o.reflectType) {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+StructLoop:
|
|
|
|
|
+ for i := 0; i < o.reflectValue.Elem().NumField(); i++ {
|
|
|
|
|
+ field := o.reflectValue.Elem().Field(i)
|
|
|
|
|
+ fieldType := field.Type()
|
|
|
|
|
+ fieldTag := o.reflectType.Elem().Field(i).Tag
|
|
|
|
|
+ fieldName := o.reflectType.Elem().Field(i).Name
|
|
|
|
|
+ tag, err := parseTag(string(fieldTag))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "unexpected tag format `%s` for field %s in type %s",
|
|
|
|
|
+ string(fieldTag),
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Skip fields without a tag.
|
|
|
|
|
+ if tag == nil {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Cannot be used with unexported fields.
|
|
|
|
|
+ if !field.CanSet() {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "inject requested on unexported field %s in type %s",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Inline tag on anything besides a struct is considered invalid.
|
|
|
|
|
+ if tag.Inline && fieldType.Kind() != reflect.Struct {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "inline requested on non inlined field %s in type %s",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Don't overwrite existing values.
|
|
|
|
|
+ if !isNilOrZero(field, fieldType) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Named injects must have been explicitly provided.
|
|
|
|
|
+ if tag.Name != "" {
|
|
|
|
|
+ existing := g.named[tag.Name]
|
|
|
|
|
+ if existing == nil {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "did not find object named %s required by field %s in type %s",
|
|
|
|
|
+ tag.Name,
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if !existing.reflectType.AssignableTo(fieldType) {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "object named %s of type %s is not assignable to field %s (%s) in type %s",
|
|
|
|
|
+ tag.Name,
|
|
|
|
|
+ fieldType,
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ existing.reflectType,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ field.Set(reflect.ValueOf(existing.Value))
|
|
|
|
|
+ if g.Logger != nil {
|
|
|
|
|
+ g.Logger.Debugf(
|
|
|
|
|
+ "assigned %s to field %s in %s",
|
|
|
|
|
+ existing,
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ o.addDep(fieldName, existing)
|
|
|
|
|
+ continue StructLoop
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Inline struct values indicate we want to traverse into it, but not
|
|
|
|
|
+ // inject itself. We require an explicit "inline" tag for this to work.
|
|
|
|
|
+ if fieldType.Kind() == reflect.Struct {
|
|
|
|
|
+ if tag.Private {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "cannot use private inject on inline struct on field %s in type %s",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if !tag.Inline {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "inline struct on field %s in type %s requires an explicit \"inline\" tag",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ err := g.Provide(&Object{
|
|
|
|
|
+ Value: field.Addr().Interface(),
|
|
|
|
|
+ private: true,
|
|
|
|
|
+ embedded: o.reflectType.Elem().Field(i).Anonymous,
|
|
|
|
|
+ })
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Interface injection is handled in a second pass.
|
|
|
|
|
+ if fieldType.Kind() == reflect.Interface {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Maps are created and required to be private.
|
|
|
|
|
+ if fieldType.Kind() == reflect.Map {
|
|
|
|
|
+ if !tag.Private {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "inject on map field %s in type %s must be named or private",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ field.Set(reflect.MakeMap(fieldType))
|
|
|
|
|
+ if g.Logger != nil {
|
|
|
|
|
+ g.Logger.Debugf(
|
|
|
|
|
+ "made map for field %s in %s",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Can only inject Pointers from here on.
|
|
|
|
|
+ if !isStructPtr(fieldType) {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "found inject tag on unsupported field %s in type %s",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Unless it's a private inject, we'll look for an existing instance of the
|
|
|
|
|
+ // same type.
|
|
|
|
|
+ if !tag.Private {
|
|
|
|
|
+ for _, existing := range g.unnamed {
|
|
|
|
|
+ if existing.private {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ if existing.reflectType.AssignableTo(fieldType) {
|
|
|
|
|
+ field.Set(reflect.ValueOf(existing.Value))
|
|
|
|
|
+ if g.Logger != nil {
|
|
|
|
|
+ g.Logger.Debugf(
|
|
|
|
|
+ "assigned existing %s to field %s in %s",
|
|
|
|
|
+ existing,
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ o.addDep(fieldName, existing)
|
|
|
|
|
+ continue StructLoop
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ newValue := reflect.New(fieldType.Elem())
|
|
|
|
|
+ newObject := &Object{
|
|
|
|
|
+ Value: newValue.Interface(),
|
|
|
|
|
+ private: tag.Private,
|
|
|
|
|
+ created: true,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add the newly ceated object to the known set of objects.
|
|
|
|
|
+ err = g.Provide(newObject)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Finally assign the newly created object to our field.
|
|
|
|
|
+ field.Set(newValue)
|
|
|
|
|
+ if g.Logger != nil {
|
|
|
|
|
+ g.Logger.Debugf(
|
|
|
|
|
+ "assigned newly created %s to field %s in %s",
|
|
|
|
|
+ newObject,
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ o.addDep(fieldName, newObject)
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (g *Graph) populateUnnamedInterface(o *Object) error {
|
|
|
|
|
+ // Ignore named value types.
|
|
|
|
|
+ if o.Name != "" && !isStructPtr(o.reflectType) {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for i := 0; i < o.reflectValue.Elem().NumField(); i++ {
|
|
|
|
|
+ field := o.reflectValue.Elem().Field(i)
|
|
|
|
|
+ fieldType := field.Type()
|
|
|
|
|
+ fieldTag := o.reflectType.Elem().Field(i).Tag
|
|
|
|
|
+ fieldName := o.reflectType.Elem().Field(i).Name
|
|
|
|
|
+ tag, err := parseTag(string(fieldTag))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "unexpected tag format `%s` for field %s in type %s",
|
|
|
|
|
+ string(fieldTag),
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Skip fields without a tag.
|
|
|
|
|
+ if tag == nil {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // We only handle interface injection here. Other cases including errors
|
|
|
|
|
+ // are handled in the first pass when we inject pointers.
|
|
|
|
|
+ if fieldType.Kind() != reflect.Interface {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Interface injection can't be private because we can't instantiate new
|
|
|
|
|
+ // instances of an interface.
|
|
|
|
|
+ if tag.Private {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "found private inject tag on interface field %s in type %s",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Don't overwrite existing values.
|
|
|
|
|
+ if !isNilOrZero(field, fieldType) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Named injects must have already been handled in populateExplicit.
|
|
|
|
|
+ if tag.Name != "" {
|
|
|
|
|
+ panic(fmt.Sprintf("unhandled named instance with name %s", tag.Name))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Find one, and only one assignable value for the field.
|
|
|
|
|
+ var found *Object
|
|
|
|
|
+ for _, existing := range g.unnamed {
|
|
|
|
|
+ if existing.private {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ if existing.reflectType.AssignableTo(fieldType) {
|
|
|
|
|
+ if found != nil {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "found two assignable values for field %s in type %s. one type "+
|
|
|
|
|
+ "%s with value %v and another type %s with value %v",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ found.reflectType,
|
|
|
|
|
+ found.Value,
|
|
|
|
|
+ existing.reflectType,
|
|
|
|
|
+ existing.reflectValue,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ found = existing
|
|
|
|
|
+ field.Set(reflect.ValueOf(existing.Value))
|
|
|
|
|
+ if g.Logger != nil {
|
|
|
|
|
+ g.Logger.Debugf(
|
|
|
|
|
+ "assigned existing %s to interface field %s in %s",
|
|
|
|
|
+ existing,
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ o.addDep(fieldName, existing)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If we didn't find an assignable value, we're missing something.
|
|
|
|
|
+ if found == nil {
|
|
|
|
|
+ return fmt.Errorf(
|
|
|
|
|
+ "found no assignable value for field %s in type %s",
|
|
|
|
|
+ o.reflectType.Elem().Field(i).Name,
|
|
|
|
|
+ o.reflectType,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Objects returns all known objects, named as well as unnamed. The returned
|
|
|
|
|
+// elements are not in a stable order.
|
|
|
|
|
+func (g *Graph) Objects() []*Object {
|
|
|
|
|
+ objects := make([]*Object, 0, len(g.unnamed)+len(g.named))
|
|
|
|
|
+ for _, o := range g.unnamed {
|
|
|
|
|
+ if !o.embedded {
|
|
|
|
|
+ objects = append(objects, o)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, o := range g.named {
|
|
|
|
|
+ if !o.embedded {
|
|
|
|
|
+ objects = append(objects, o)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // randomize to prevent callers from relying on ordering
|
|
|
|
|
+ for i := 0; i < len(objects); i++ {
|
|
|
|
|
+ j := rand.Intn(i + 1)
|
|
|
|
|
+ objects[i], objects[j] = objects[j], objects[i]
|
|
|
|
|
+ }
|
|
|
|
|
+ return objects
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+var (
|
|
|
|
|
+ injectOnly = &tag{}
|
|
|
|
|
+ injectPrivate = &tag{Private: true}
|
|
|
|
|
+ injectInline = &tag{Inline: true}
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+type tag struct {
|
|
|
|
|
+ Name string
|
|
|
|
|
+ Inline bool
|
|
|
|
|
+ Private bool
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func parseTag(t string) (*tag, error) {
|
|
|
|
|
+ found, value, err := structtag.Extract("inject", t)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ if !found {
|
|
|
|
|
+ return nil, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ if value == "" {
|
|
|
|
|
+ return injectOnly, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ if value == "inline" {
|
|
|
|
|
+ return injectInline, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ if value == "private" {
|
|
|
|
|
+ return injectPrivate, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ return &tag{Name: value}, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func isStructPtr(t reflect.Type) bool {
|
|
|
|
|
+ return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func isNilOrZero(v reflect.Value, t reflect.Type) bool {
|
|
|
|
|
+ switch v.Kind() {
|
|
|
|
|
+ default:
|
|
|
|
|
+ return reflect.DeepEqual(v.Interface(), reflect.Zero(t).Interface())
|
|
|
|
|
+ case reflect.Interface, reflect.Ptr:
|
|
|
|
|
+ return v.IsNil()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|