github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/injector/resolver.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 11 "github.com/insolar/vanilla/throw" 12 ) 13 14 func NewDependencyResolver(target interface{}, globalParent DependencyRegistry, localParent DependencyRegistry, 15 onPutFn func(id string, v interface{}, from DependencyOrigin)) DependencyResolver { 16 if target == nil { 17 panic(throw.IllegalValue()) 18 } 19 return DependencyResolver{target: target, globalParent: globalParent, localParent: localParent, onPutFn: onPutFn} 20 } 21 22 type DependencyOrigin uint8 23 24 const ( 25 DependencyFromLocal DependencyOrigin = 1 << iota 26 DependencyFromProvider 27 ) 28 29 type DependencyResolver struct { 30 globalParent DependencyRegistry 31 localParent DependencyRegistry 32 target interface{} 33 implMap map[reflect.Type]interface{} 34 resolved map[string]interface{} 35 onPutFn func(id string, v interface{}, from DependencyOrigin) 36 } 37 38 func (p *DependencyResolver) IsZero() bool { 39 return p.target == nil 40 } 41 42 func (p *DependencyResolver) IsEmpty() bool { 43 return len(p.resolved) == 0 44 } 45 46 func (p *DependencyResolver) Count() int { 47 return len(p.resolved) 48 } 49 50 func (p *DependencyResolver) Target() interface{} { 51 return p.target 52 } 53 54 func (p *DependencyResolver) ResolveAndReplace(overrides map[string]interface{}) { 55 for id, v := range overrides { 56 if id == "" { 57 panic(throw.IllegalValue()) 58 } 59 p.resolveAndPut(id, v, DependencyFromLocal) 60 } 61 } 62 63 func (p *DependencyResolver) ResolveAndMerge(values map[string]interface{}) { 64 for id, v := range values { 65 if id == "" { 66 panic(throw.IllegalValue()) 67 } 68 if _, ok := p.resolved[id]; ok { 69 continue 70 } 71 p.resolveAndPut(id, v, DependencyFromLocal) 72 } 73 } 74 75 func (p *DependencyResolver) FindDependency(id string) (interface{}, bool) { 76 if id == "" { 77 panic(throw.IllegalValue()) 78 } 79 if v, ok := p.resolved[id]; ok { // allows nil values 80 return v, true 81 } 82 83 v, ok, _ := p.getFromParent(id) 84 return v, ok 85 } 86 87 func (p *DependencyResolver) FindImplementation(t reflect.Type, checkAmbiguous bool) (interface{}, error) { 88 if v := p.implMap[t]; v != nil { 89 return v, nil 90 } else if p.implMap == nil { 91 p.implMap = map[reflect.Type]interface{}{} 92 } 93 94 var from DependencyOrigin 95 id := "" 96 var v interface{} 97 98 if scanner, ok := p.localParent.(ScanDependencyRegistry); ok { 99 var err error 100 id, v, err = p.findImpl(scanner, t, checkAmbiguous, "") 101 switch { 102 case err != nil: 103 return nil, err 104 case v != nil: 105 from |= DependencyFromLocal 106 } 107 } 108 109 switch scanner, ok := p.globalParent.(ScanDependencyRegistry); { 110 case !ok: 111 case v == nil: 112 var err error 113 id, v, err = p.findImpl(scanner, t, checkAmbiguous, "") 114 switch { 115 case err != nil: 116 return nil, err 117 case v == nil: 118 return nil, nil 119 } 120 case checkAmbiguous: 121 if _, _, err := p.findImpl(scanner, t, true, id); err != nil { 122 return nil, err 123 } 124 } 125 126 if from&DependencyFromLocal != 0 { 127 p.putResolved(id, v, from) 128 } 129 p.implMap[t] = v 130 131 return v, nil 132 } 133 134 func (p *DependencyResolver) findImpl(scanner ScanDependencyRegistry, t reflect.Type, checkAmbiguous bool, fid string) (id string, v interface{}, err error) { 135 id = fid 136 scanner.ScanDependencies(func(xid string, xv interface{}) bool { 137 switch { 138 case xv == nil || xid == "": 139 return false 140 case !reflect.ValueOf(xv).Type().AssignableTo(t): 141 return false 142 case id != "": 143 err = throw.E("ambiguous dependency", struct { 144 ExpectedType reflect.Type 145 ID1, ID2 string 146 }{ 147 t, id, xid, 148 }) 149 return true 150 default: 151 id, v = xid, xv 152 return !checkAmbiguous 153 } 154 }) 155 return 156 } 157 158 func (p *DependencyResolver) GetResolvedDependency(id string) (interface{}, bool) { 159 if id == "" { 160 panic(throw.IllegalValue()) 161 } 162 return p.getResolved(id) 163 } 164 165 func (p *DependencyResolver) getFromParent(id string) (interface{}, bool, DependencyOrigin) { 166 if p.localParent != nil { 167 if v, ok := p.localParent.FindDependency(id); ok { 168 return v, true, DependencyFromLocal 169 } 170 } 171 if p.globalParent != nil { 172 if v, ok := p.globalParent.FindDependency(id); ok { 173 return v, true, 0 174 } 175 } 176 return nil, false, 0 177 } 178 179 func (p *DependencyResolver) getResolved(id string) (interface{}, bool) { 180 if v, ok := p.resolved[id]; ok { // allows nil values 181 return v, true 182 } 183 if v, ok, from := p.getFromParent(id); ok { 184 return p.resolveAndPut(id, v, from), true 185 } 186 187 return nil, false 188 } 189 190 func (p *DependencyResolver) resolveAndPut(id string, v interface{}, from DependencyOrigin) interface{} { 191 if dp, ok := v.(DependencyProviderFunc); ok { 192 p._putResolved(id, nil) // guard for resolve loop 193 v = dp(p.target, id, p.GetResolvedDependency) 194 p.putResolved(id, v, from|DependencyFromProvider) 195 } else if from&DependencyFromLocal != 0 { 196 p.putResolved(id, v, from) 197 } 198 return v 199 } 200 201 func (p *DependencyResolver) putResolved(id string, v interface{}, from DependencyOrigin) { 202 p._putResolved(id, v) 203 if p.onPutFn != nil { 204 p.onPutFn(id, v, from) 205 } 206 } 207 208 func (p *DependencyResolver) _putResolved(id string, v interface{}) { 209 if p.resolved == nil { 210 p.resolved = make(map[string]interface{}) 211 } 212 p.resolved[id] = v 213 } 214 215 func (p *DependencyResolver) CopyResolved() map[string]interface{} { 216 n := len(p.resolved) 217 if n == 0 { 218 return nil 219 } 220 result := make(map[string]interface{}, n) 221 222 for id, v := range p.resolved { 223 result[id] = v 224 } 225 226 return result 227 } 228 229 func (p *DependencyResolver) Flush() map[string]interface{} { 230 n := len(p.resolved) 231 if n == 0 { 232 return nil 233 } 234 m := p.resolved 235 p.resolved = nil 236 return m 237 }