Files
grafana/vendor/github.com/facebookgo/inject/inject.go
T
Carl Bergquist 28f7b6dad1 Enable Grafana extensions at build time. (#11752)
* extensions: import and build

* bus: use predefined error

* enterprise: build script for enterprise packages

* poc: auto registering services and dependency injection

(cherry picked from commit b5b1ef875f905473af41e49f8071cb9028edc845)

* poc: backend services registry progress

(cherry picked from commit 97be69725881241bfbf1e7adf0e66801d6b0af3d)

* poc: minor update

(cherry picked from commit 03d7a6888b81403f458b94305792e075568f0794)

* ioc: introduce manuel ioc

* enterprise: adds setting for enterprise

* build: test and build specific ee commit

* cleanup: test testing code

* removes example hello service
2018-04-27 13:41:58 +02:00

577 lines
14 KiB
Go

// 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()
}
}