github.com/wfusion/gofusion@v1.1.14/config/component.go (about) 1 package config 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "sync" 8 9 "github.com/pkg/errors" 10 "github.com/wfusion/gofusion/common/constant" 11 "github.com/wfusion/gofusion/common/utils" 12 "github.com/wfusion/gofusion/common/utils/clone" 13 ) 14 15 // common component names 16 const ( 17 ComponentApp = "App" 18 ComponentDebug = "Debug" 19 ComponentCrypto = "Crypto" 20 ComponentMetrics = "Metrics" 21 ComponentLog = "Log" 22 ComponentDB = "DB" 23 ComponentRedis = "Redis" 24 ComponentMongo = "Mongo" 25 ComponentI18n = "I18n" 26 ComponentLock = "Lock" 27 ComponentMessageQueue = "MQ" 28 ComponentHttp = "Http" 29 ComponentCache = "Cache" 30 ComponentCron = "Cron" 31 ComponentAsync = "Async" 32 ComponentGoroutinePool = "GoroutinePool" 33 ) 34 35 const ( 36 DefaultInstanceKey = "default" 37 ) 38 39 var ( 40 // componentOrder common component setup order 41 componentOrder = []string{ 42 ComponentApp, 43 ComponentDebug, 44 ComponentCrypto, 45 ComponentLog, 46 ComponentMetrics, 47 ComponentRedis, 48 ComponentCache, 49 ComponentDB, 50 ComponentMongo, 51 ComponentI18n, 52 ComponentLock, 53 ComponentMessageQueue, 54 ComponentAsync, 55 ComponentGoroutinePool, 56 ComponentCron, 57 ComponentHttp, 58 } 59 60 componentLocker sync.RWMutex 61 components []*componentItem 62 ) 63 64 func indexComponent(name string) (idx int) { 65 for idx = 0; idx < len(componentOrder); idx++ { 66 if componentOrder[idx] == name { 67 return 68 } 69 } 70 idx = -1 71 return 72 } 73 74 type Component struct { 75 name string 76 tag string 77 constructor reflect.Value 78 constructorInputType reflect.Type 79 isCore bool 80 flagString *string 81 } 82 83 func (c *Component) Clone() (r *Component) { 84 return &Component{ 85 name: c.name, 86 tag: c.tag, 87 constructor: c.constructor, 88 constructorInputType: c.constructorInputType, 89 isCore: c.isCore, 90 flagString: c.flagString, 91 } 92 } 93 94 type options struct { 95 tagList []string 96 isCoreComponent bool 97 flagValue *string 98 } 99 100 type ComponentOption func(*options) 101 102 func newOptions() *options { 103 return &options{} 104 } 105 106 // WithTag set component struct tags 107 func WithTag(name, val string) ComponentOption { 108 return func(opt *options) { 109 opt.tagList = append(opt.tagList, fmt.Sprintf(`%s:"%s"`, name, val)) 110 } 111 } 112 113 // WithCore mark component as core component, they must be init first 114 func WithCore() ComponentOption { 115 return func(opt *options) { 116 opt.isCoreComponent = true 117 } 118 } 119 120 func WithFlag(flagValue *string) ComponentOption { 121 return func(o *options) { 122 o.flagValue = flagValue 123 } 124 } 125 126 type componentItem struct { 127 name string 128 constructor any 129 opt []ComponentOption 130 } 131 132 func AddComponent(name string, constructor any, opts ...ComponentOption) { 133 componentLocker.Lock() 134 defer componentLocker.Unlock() 135 parseConstructor(constructor) 136 components = append(components, &componentItem{name, constructor, opts}) 137 } 138 139 func getComponents() []*componentItem { 140 componentLocker.RLock() 141 defer componentLocker.RUnlock() 142 return clone.Clone(components) 143 } 144 145 func parseConstructor(fn any) (fnVal reflect.Value, input reflect.Type) { 146 fnVal = reflect.ValueOf(fn) 147 typ := reflect.TypeOf(fn) 148 if typ.Kind() != reflect.Func { 149 panic(errors.New("component constructor should be a function")) 150 } 151 152 // check output 153 if typ.NumOut() != 1 { 154 panic(errors.New("component constructor should return one finalizer function")) 155 } 156 retTyp := typ.Out(0) 157 if retTyp.Kind() != reflect.Func { 158 panic(errors.New("component constructor should return one finalizer function")) 159 } 160 if retTyp.NumIn() != 0 { 161 panic(errors.New("component constructor should return one finalizer function looks like func()")) 162 } 163 164 // check input 165 fnType := fnVal.Type() 166 if n := fnType.NumIn(); n != 1 && (!fnType.IsVariadic() && n != 3) { 167 panic(errors.New("component constructor should receive input looks like " + 168 "func(context.Context), func(context.Context, *serializableConf, ...utils.OptionExtender)")) 169 } 170 if fnType.In(0) != constant.ContextType { 171 panic(errors.New("component constructor should receive context.Context as first input " + 172 "looks like func(context.Context), func(context.Context, *serializableConf, ...utils.OptionExtender)")) 173 } 174 175 // wrapper 176 switch typ.NumIn() { 177 case 1: 178 input = reflect.TypeOf(int(0)) 179 fnVal = reflect.ValueOf(func(ctx context.Context, mock int, _ ...utils.OptionExtender) func() { 180 out := reflect.ValueOf(fn).Call([]reflect.Value{reflect.ValueOf(ctx)}) 181 if retfn := out[0]; retfn.IsNil() { 182 return nil 183 } else if obj := retfn.Interface(); obj == nil { 184 return nil 185 } else if fn, ok := obj.(func()); !ok { 186 return nil 187 } else { 188 return fn 189 } 190 }) 191 case 3: 192 input = typ.In(1) 193 argsType := typ.In(2) 194 if argsType.Kind() != reflect.Slice || 195 argsType.Elem() != reflect.TypeOf((*utils.OptionExtender)(nil)).Elem() { 196 panic(errors.New("component constructor only receive utils.OptionExtender variadic input")) 197 } 198 default: 199 panic(errors.New("component constructor should receive one or three inputs looks like " + 200 "func(context.Context), func(context.Context, *serializableConf, ...utils.OptionExtender)")) 201 } 202 return 203 }