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 }