github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xmodule/xmodule_auto.go (about) 1 package xmodule 2 3 import ( 4 "errors" 5 "fmt" 6 "github.com/Aoi-hosizora/ahlib/xerror" 7 "reflect" 8 ) 9 10 // _moduleTagName is the "module" tag name which is used to regard struct field as a module. 11 const _moduleTagName = "module" 12 13 const ( 14 panicInjectIntoNilValue = "xmodule: inject into nil value" 15 panicInjectIntoNonStructPtr = "xmodule: inject into non-struct pointer" 16 panicInvalidProvider = "xmodule: using nil or invalid module provider" 17 18 errRequiredModuleNotFound = "xmodule: module '%s' required by injectee '%s' is not found" 19 errMismatchedModuleType = "xmodule: module type '%s' mismatches with field type '%s'" 20 errModulesCycleDependency = "xmodule: given provided modules have cycle dependency" 21 ) 22 23 // ================= 24 // injection related 25 // ================= 26 27 // Inject injects into given injectee's module fields, returns error if there are some fields can not be injected (possible reasons: specific 28 // module is not found, module type mismatches with field), panics when injectee passed is nil or not a structure pointer. Note that if error 29 // is returned, remaining fields will still be injected as usual. 30 // 31 // Example: 32 // type Struct struct { 33 // unexportedField string // -> ignore (unexported) 34 // ExportedField1 string // -> ignore (no module tag) 35 // ExportedField2 string `module:""` // -> ignore (module tag is empty) 36 // ExportedField3 string `module:"-"` // -> ignore (module tag is "-") 37 // ExportedField4 string `module:"name"` // -> inject by name 38 // ExportedField5 string `module:"~"` // -> inject by type or intf 39 // } 40 // m := NewModuleContainer() 41 // all := m.Inject(&Struct{}) 42 func (m *ModuleContainer) Inject(injectee interface{}) error { 43 return coreInject(m, injectee, false) 44 } 45 46 // MustInject injects into given injectee's module fields, panics when injectee passed is nil or not a structure pointer, or there are some fields 47 // can not be injected for several reasons. Note that remaining fields will stop injecting once error happened. See Inject for more details. 48 func (m *ModuleContainer) MustInject(injectee interface{}) { 49 _ = coreInject(m, injectee, true) 50 } 51 52 // coreInject is the core implementation for Inject and MustInject. 53 func coreInject(mc *ModuleContainer, injectee interface{}, force bool) error { 54 // check parameters 55 if injectee == nil { 56 panic(panicInjectIntoNilValue) 57 } 58 val := reflect.ValueOf(injectee) 59 typ := val.Type() 60 if typ.Kind() != reflect.Ptr { 61 panic(panicInjectIntoNonStructPtr) 62 } 63 injecteeName := typ.String() 64 val = val.Elem() 65 typ = typ.Elem() 66 if typ.Kind() != reflect.Struct { 67 panic(panicInjectIntoNonStructPtr) 68 } 69 70 // inject to struct fields 71 errs := injectToStructFields(mc, typ, val, injecteeName, true, force) 72 return xerror.Combine(errs...) 73 } 74 75 // injectToStructFields tries to inject modules to given structure, returns errors when some fields can not be injected. 76 func injectToStructFields(mc *ModuleContainer, structType reflect.Type, structValue reflect.Value, injecteeTypeName string, lock, force bool) []error { 77 var errs []error 78 totalCount := 0 79 injectedCount := 0 80 81 // for each struct field 82 for idx := 0; idx < structType.NumField(); idx++ { 83 sf, sv := structType.Field(idx), structValue.Field(idx) 84 moduleTag := sf.Tag.Get(_moduleTagName) 85 if moduleTag == "" || moduleTag == "-" { 86 continue // not a module field 87 } 88 89 // inject to module field 90 totalCount++ 91 err := injectToSingleField(mc, moduleTag, sf.Type, sv, injecteeTypeName, lock) 92 if err != nil { 93 if force { 94 panic(err.Error()) 95 } 96 errs = append(errs, err) 97 continue // no force -> just continue 98 } 99 100 mc.logger.InjField(moduleTag, injecteeTypeName, sf.Name, sf.Type.String()) 101 injectedCount++ 102 } 103 104 if totalCount > 0 { 105 mc.logger.InjFinish(injecteeTypeName, injectedCount, totalCount) 106 } 107 return errs 108 } 109 110 // injectToSingleField checks whether the module for given field exists and type matches, and injects it to given field if check passed. 111 func injectToSingleField(mc *ModuleContainer, fieldTag string, fieldType reflect.Type, fieldValue reflect.Value, injecteeTypeName string, lock bool) error { 112 // generate key by field tag and type 113 key := mkeyFromField(fieldTag, fieldType) // by name (...), type or intf (~) 114 115 // check module existence 116 if lock { 117 mc.mu.RLock() 118 } 119 module, exist := mc.modules[key] 120 if lock { 121 mc.mu.RUnlock() 122 } 123 if !exist { 124 // specific module is not found 125 return fmt.Errorf(errRequiredModuleNotFound, fieldType.String(), injecteeTypeName) 126 } 127 128 // check module injectable 129 moduleVal := reflect.ValueOf(module) 130 moduleType := moduleVal.Type() 131 if !moduleType.AssignableTo(fieldType) { 132 // module type mismatches with field 133 return fmt.Errorf(errMismatchedModuleType, moduleType.String(), fieldType.String()) 134 } 135 136 // check field assignable, inject module to field 137 if fieldValue.CanSet() { 138 fieldValue.Set(moduleVal) 139 } 140 return nil 141 } 142 143 // ==================== 144 // auto provide related 145 // ==================== 146 147 // ModuleProvider represents a type for module provider used by AutoProvide. Note that you must create this value by NameProvider, TypeProvider 148 // or IntfProvider, otherwise it may panic when invoking AutoProvide. 149 type ModuleProvider struct { 150 mod interface{} 151 key mkey 152 153 modType reflect.Type 154 byName bool 155 byType bool 156 byIntf bool 157 158 depKeys []mkey // late, update by analyseDependency 159 } 160 161 // NameProvider creates a ModuleProvider, it can be used to provide a module using given ModuleName. 162 func NameProvider(name ModuleName, module interface{}) *ModuleProvider { 163 ensureModuleName(name) 164 typ := ensureModuleType(module) 165 return &ModuleProvider{mod: module, key: nameKey(name), modType: typ, byName: true} 166 } 167 168 // TypeProvider creates a ModuleProvider, it can be used to provide a module using its type. 169 func TypeProvider(module interface{}) *ModuleProvider { 170 typ := ensureModuleType(module) 171 return &ModuleProvider{mod: module, key: typeKey(typ), modType: typ, byType: true} 172 } 173 174 // IntfProvider creates a ModuleProvider, it can be used to provide a module using given interface pointer type. 175 func IntfProvider(interfacePtr interface{}, moduleImpl interface{}) *ModuleProvider { 176 intfType := ensureInterfacePtr(interfacePtr) // interface type 177 modType := ensureModuleTypeWithInterface(moduleImpl, intfType) // module type 178 return &ModuleProvider{mod: moduleImpl, key: typeKey(intfType), modType: modType, byIntf: true} 179 } 180 181 // AutoProvide processes with given ModuleProvider-s, injects them if necessary (must be a pointer of struct), and provides them in dependency 182 // order, returns error if some fields from providers can not be injected (see Inject for more details), or some dependent modules is not found, 183 // or cycle dependency happens, panics when using invalid provider. 184 // 185 // Example: 186 // wellKnownList := []int{...} 187 // type Service struct { 188 // WellKnownList []int `module:"list"` 189 // AnotherService *ServiceB `module:"~"` 190 // Implement Interface `module:"~"` 191 // LocalVariable string // a local variable for Service 192 // } 193 // m := NewModuleContainer() 194 // _ = m.AutoProvide( 195 // TypeProvider(&Service{LocalVariable: "..."}), 196 // TypeProvider(&ServiceB{...}), 197 // NameProvider("list", wellKnownList), 198 // IntfProvider((*Interface)(nil), &Implement{}), 199 // ) 200 // _ = m.MustGetByType(&Service{}).(*Service) 201 func (m *ModuleContainer) AutoProvide(providers ...*ModuleProvider) error { 202 return coreAutoProvide(m, providers) 203 } 204 205 // MustAutoProvide processes with given ModuleProvider-s, injects them if necessary and provides them in dependency order, panics when error happens. 206 // See AutoProvide for more details. 207 func (m *ModuleContainer) MustAutoProvide(providers ...*ModuleProvider) { 208 err := coreAutoProvide(m, providers) 209 if err != nil { 210 panic(err.Error()) 211 } 212 } 213 214 // coreAutoProvide is the core implementation for AutoProvide and MustAutoProvide. 215 func coreAutoProvide(mc *ModuleContainer, providers []*ModuleProvider) error { 216 indeps, depGraph := analyseDependency(providers) 217 mc.mu.Lock() // modifying container is safe 218 defer mc.mu.Unlock() 219 220 // 1. independent providers 221 if len(indeps) > 0 { 222 for _, p := range indeps { 223 _ = injectAndProvide(mc, p, false) // never error 224 } 225 } 226 227 // 2. dependent providers 228 for len(depGraph) > 0 { 229 // providable module: no dependent in graph || all dependents have been provided 230 providable, err := findProvidableModules(mc, depGraph) 231 if err != nil { 232 return err 233 } 234 for _, p := range providable { 235 err := injectAndProvide(mc, p, true) 236 if err != nil { 237 return err 238 } 239 // just delete the current module provider from graph 240 delete(depGraph, p.key) 241 } 242 } 243 244 return nil 245 } 246 247 // analyseDependency checks given ModuleProvider-s, analyses and splits them into independent providers and module dependency graph. 248 func analyseDependency(providers []*ModuleProvider) (indeps []*ModuleProvider, depGraph map[mkey]*ModuleProvider) { 249 indeps = make([]*ModuleProvider, 0) 250 depGraph = make(map[mkey]*ModuleProvider, 0) 251 252 for _, p := range providers { 253 // check current provider 254 if p == nil { 255 continue 256 } 257 if p.mod == nil || (p.key.name == "" && p.key.typ == nil) { 258 panic(panicInvalidProvider) 259 } 260 261 // extract module's dependents 262 if p.modType.Kind() == reflect.Ptr && p.modType.Elem().Kind() == reflect.Struct { 263 typ := p.modType.Elem() 264 for idx := 0; idx < typ.NumField(); idx++ { 265 sf := typ.Field(idx) 266 moduleTag := sf.Tag.Get(_moduleTagName) 267 if moduleTag == "" || moduleTag == "-" { 268 continue 269 } 270 key := mkeyFromField(moduleTag, sf.Type) // by name (...), type or intf (~) 271 p.depKeys = append(p.depKeys, key) 272 } 273 } 274 275 // add current provider to result 276 if len(p.depKeys) == 0 { 277 indeps = append(indeps, p) // module that have no dependent 278 } else { 279 depGraph[p.key] = p // add module to dependency graph 280 } 281 } 282 return indeps, depGraph 283 } 284 285 // findProvidableModules finds providable modules in current dependency graph, returns error if cycle dependency happens, or some dependents not found. 286 func findProvidableModules(mc *ModuleContainer, graph map[mkey]*ModuleProvider) ([]*ModuleProvider, error) { 287 // find nodes which may be able to be provided 288 providable := make([]*ModuleProvider, 0) 289 for _, p := range graph { 290 out := false 291 for _, depKey := range p.depKeys { 292 if _, ok := graph[depKey]; ok { 293 out = true 294 break 295 } 296 } 297 if !out { 298 // node that has zero out degree -> no dependent in graph currently 299 providable = append(providable, p) 300 } 301 } 302 if len(graph) > 0 && len(providable) == 0 { 303 return nil, errors.New(errModulesCycleDependency) 304 } 305 306 // check existence of these nodes all dependents 307 for _, p := range providable { 308 for _, depKey := range p.depKeys { // these dependents must not be in graph currently 309 if _, ok := mc.modules[depKey]; !ok { 310 return nil, fmt.Errorf(errRequiredModuleNotFound, depKey.String(), p.modType.String()) 311 } 312 } 313 } 314 return providable, nil 315 } 316 317 // injectAndProvide injects to given ModuleProvider only for struct pointer type, and provides it to ModuleContainer for all types. 318 func injectAndProvide(mc *ModuleContainer, p *ModuleProvider, needInject bool) error { 319 // check module injectable and do inject 320 if needInject { 321 val := reflect.ValueOf(p.mod) 322 typ := val.Type() 323 if typ.Kind() == reflect.Ptr { 324 injecteeName := typ.String() 325 typ, val = typ.Elem(), val.Elem() 326 if typ.Kind() == reflect.Struct { 327 // inject something to module first 328 errs := injectToStructFields(mc, typ, val, injecteeName, false, false) 329 if len(errs) > 0 { 330 return xerror.Combine(errs...) 331 } 332 } 333 } 334 } 335 336 // provide module after injecting 337 mc.modules[p.key] = p.mod 338 switch { 339 case p.byName: 340 mc.logger.PrvName(p.key.name.String(), p.modType.String()) 341 case p.byType: 342 mc.logger.PrvType(p.key.typ.String()) 343 case p.byIntf: 344 mc.logger.PrvIntf(p.key.typ.String(), p.modType.String()) 345 } 346 return nil 347 }