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  }