github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/injector/injector.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package injector
     7  
     8  import (
     9  	"reflect"
    10  	"strings"
    11  
    12  	"github.com/insolar/vanilla/throw"
    13  )
    14  
    15  func GetDefaultInjectionID(v interface{}) string {
    16  	return GetDefaultInjectionIDByType(reflect.TypeOf(v))
    17  }
    18  
    19  func GetDefaultInjectionIDByType(vt reflect.Type) string {
    20  	return strings.TrimLeft(vt.String(), "*")
    21  }
    22  
    23  func NewDependencyInjector(target interface{}, globalParent DependencyRegistry, localParent DependencyRegistry) DependencyInjector {
    24  	resolver := NewDependencyResolver(target, globalParent, localParent, nil)
    25  	return NewDependencyInjectorFor(&resolver)
    26  }
    27  
    28  func NewDependencyInjectorFor(resolver *DependencyResolver) DependencyInjector {
    29  	if resolver == nil || resolver.IsZero() {
    30  		panic(throw.IllegalValue())
    31  	}
    32  	return DependencyInjector{resolver}
    33  }
    34  
    35  type DependencyInjector struct {
    36  	resolver *DependencyResolver
    37  }
    38  
    39  func (u DependencyInjector) IsZero() bool {
    40  	return u.resolver.IsZero()
    41  }
    42  
    43  func (u DependencyInjector) IsEmpty() bool {
    44  	return u.resolver.IsEmpty()
    45  }
    46  
    47  func (u DependencyInjector) MustInject(varRef interface{}) {
    48  	if err := u.Inject(varRef); err != nil {
    49  		panic(throw.WithStack(err))
    50  	}
    51  }
    52  
    53  func (u DependencyInjector) MustInjectByID(id string, varRef interface{}) {
    54  	if err := u.InjectByID(id, varRef); err != nil {
    55  		panic(throw.WithStack(err))
    56  	}
    57  }
    58  
    59  func (u DependencyInjector) MustInjectAny(varRef interface{}) {
    60  	if err := u.InjectAny(varRef); err != nil {
    61  		panic(throw.WithStack(err))
    62  	}
    63  }
    64  
    65  func (u DependencyInjector) MustInjectAll() {
    66  	if err := u.InjectAll(); err != nil {
    67  		panic(throw.WithStack(err))
    68  	}
    69  }
    70  
    71  func (u DependencyInjector) Inject(varRef interface{}) error {
    72  	return u.tryInjectVar("", varRef)
    73  }
    74  
    75  func (u DependencyInjector) InjectByID(id string, varRef interface{}) error {
    76  	if id == "" {
    77  		panic(throw.IllegalValue())
    78  	}
    79  	return u.tryInjectVar(id, varRef)
    80  }
    81  
    82  func (u DependencyInjector) InjectAny(varRef interface{}) error {
    83  	fv := checkVarRef(varRef)
    84  	return u.injectAny(nil, fv, fv.Type(), "", "")
    85  }
    86  
    87  //nolint:interfacer
    88  func (u DependencyInjector) injectAny(tt *lazyInjectName, fv reflect.Value, ft reflect.Type, id string, fieldName string) error {
    89  	switch isNillable, isSet := u.check(fv, ft); {
    90  	case isSet:
    91  		return throw.E("dependency is set", struct { ExpectedType reflect.Type } {ft})
    92  	case id != "":
    93  		if u.resolveNameAndSet(id, fv, ft, isNillable) {
    94  			return nil
    95  		}
    96  	default:
    97  		if u.resolveTypeAndSet(tt.String(), fieldName, fv, ft, isNillable) {
    98  			return nil
    99  		}
   100  	}
   101  
   102  	if ft.Kind() == reflect.Interface {
   103  		if found, err := u.resolveImplAndSet(fv, ft); found || err != nil {
   104  			return err
   105  		}
   106  	}
   107  
   108  	return throw.E("dependency is missing", struct { ExpectedType reflect.Type } {ft})
   109  }
   110  
   111  func (u DependencyInjector) InjectAll() error {
   112  	t := reflect.Indirect(reflect.ValueOf(u.resolver.Target()))
   113  	if t.Kind() != reflect.Struct {
   114  		panic(throw.IllegalValue())
   115  	}
   116  	if !t.CanSet() {
   117  		panic(throw.FailHere("readonly"))
   118  	}
   119  	tt := t.Type()
   120  
   121  	lazyName := &lazyInjectName{t:tt}
   122  
   123  	for i := 0; i < tt.NumField(); i++ {
   124  		sf := tt.Field(i)
   125  		id, ok := sf.Tag.Lookup("inject")
   126  		if !ok {
   127  			continue
   128  		}
   129  
   130  		fv := t.Field(i)
   131  		if err := u.injectAny(lazyName, fv, sf.Type, id, sf.Name); err != nil {
   132  			return throw.WithDetails(err, struct {
   133  				Target reflect.Type
   134  				FieldName string
   135  			}{
   136  				Target: tt,
   137  				FieldName: sf.Name,
   138  			})
   139  		}
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func (u DependencyInjector) tryInjectVar(id string, varRef interface{}) error {
   146  	v := checkVarRef(varRef)
   147  	vt := v.Type()
   148  	isNillable, isSet := u.check(v, vt)
   149  
   150  	switch {
   151  	case isSet:
   152  		return throw.E("dependency is set", struct {
   153  			ExpectedType reflect.Type
   154  			ID string
   155  		} {vt, id})
   156  	case id != "":
   157  		if u.resolveNameAndSet(id, v, vt, isNillable) {
   158  			return nil
   159  		}
   160  	case u.resolveTypeAndSet(GetDefaultInjectionIDByType(vt), "", v, vt, isNillable):
   161  		return nil
   162  	}
   163  
   164  	return throw.E("dependency is missing", struct {
   165  		ExpectedType reflect.Type
   166  		ID string
   167  	} {vt, id})
   168  }
   169  
   170  func checkVarRef(varRef interface{}) reflect.Value {
   171  	if varRef == nil {
   172  		panic(throw.IllegalValue())
   173  	}
   174  
   175  	v := reflect.ValueOf(varRef)
   176  	switch {
   177  	case v.Kind() != reflect.Ptr:
   178  		panic(throw.FailHere("not a reference"))
   179  	case v.IsNil():
   180  		panic(throw.FailHere("nil reference"))
   181  	case v.CanSet() || v.CanAddr():
   182  		panic(throw.FailHere("must be a literal reference"))
   183  	}
   184  	return v.Elem()
   185  }
   186  
   187  func GetInterfaceTypeAndValue(varRef interface{}) (interface{}, reflect.Type) {
   188  	v := checkVarRef(varRef)
   189  	if v.Kind() != reflect.Interface {
   190  		panic(throw.FailHere("not an interface"))
   191  	}
   192  	vv := v.Interface()
   193  	if vv == nil {
   194  		panic(throw.FailHere("nil interface"))
   195  	}
   196  
   197  	vt := v.Type()
   198  	return vv, vt
   199  }
   200  
   201  func (u DependencyInjector) check(v reflect.Value, vt reflect.Type) (isNillable, hasValue bool) {
   202  	if !v.CanSet() {
   203  		panic(throw.FailHere("readonly"))
   204  	}
   205  
   206  	switch vt.Kind() {
   207  	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
   208  		return true, !v.IsNil()
   209  	default:
   210  		zeroValue := reflect.Zero(vt).Interface()
   211  		return false, v.Interface() != zeroValue
   212  	}
   213  }
   214  
   215  func (u DependencyInjector) resolveTypeAndSet(typeName, fieldName string, v reflect.Value, vt reflect.Type, nillable bool) bool {
   216  	if u.resolveNameAndSet(typeName, v, vt, nillable) {
   217  		return true
   218  	}
   219  
   220  	idx := strings.LastIndexByte(typeName, '.')
   221  	if idx >= 0 && u.resolveNameAndSet(typeName[idx+1:], v, vt, nillable) {
   222  		return true
   223  	}
   224  
   225  	if fieldName == "" {
   226  		return false
   227  	}
   228  	typeName = typeName + "." + fieldName
   229  
   230  	if u.resolveNameAndSet(typeName, v, vt, nillable) {
   231  		return true
   232  	}
   233  	if idx >= 0 && u.resolveNameAndSet(typeName[idx+1:], v, vt, nillable) {
   234  		return true
   235  	}
   236  	return false
   237  }
   238  
   239  func (u DependencyInjector) resolveNameAndSet(n string, v reflect.Value, vt reflect.Type, nillable bool) bool {
   240  	if len(n) == 0 {
   241  		return false
   242  	}
   243  
   244  	switch val, ok := u.resolver.getResolved(n); {
   245  	case !ok:
   246  		return false
   247  	case nillable && val == nil:
   248  		return true
   249  	default:
   250  		dv := reflect.ValueOf(val)
   251  		dt := dv.Type()
   252  		if !dt.AssignableTo(vt) {
   253  			return false
   254  		}
   255  		v.Set(dv)
   256  		return true
   257  	}
   258  }
   259  
   260  func (u DependencyInjector) resolveImplAndSet(fv reflect.Value, ft reflect.Type) (bool, error) {
   261  	switch v, err := u.resolver.FindImplementation(ft, true); {
   262  	case err != nil:
   263  		return false, err
   264  	case v == nil:
   265  		return false, nil
   266  	default:
   267  		fv.Set(reflect.ValueOf(v))
   268  		return true, nil
   269  	}
   270  }