package f5 import ( "encoding/json" "fmt" "math/rand" "q5" "reflect" ) type MetaTable interface { IsNoLoad() bool Load() PreInit1() ElementsInit(int) PostInit1() } type LoadFromKeyValue interface { LoadFromKv(map[string]interface{}) } type RawMetaTable[T any] struct { FileName string PrimKey string NoLoad bool rawList []*T } type IdMetaTable[T any] struct { RawMetaTable[T] idHash *q5.ConcurrentMap[int64, *T] } type NameMetaTable[T any] struct { RawMetaTable[T] nameHash *q5.ConcurrentMap[string, *T] } type CustomMetaTable struct { } func (this *CustomMetaTable) IsNoLoad() bool { return false } func (this *CustomMetaTable) Load() { } func (this *CustomMetaTable) PreInit1() { } func (this *CustomMetaTable) ElementsInit(int) { } func (this *CustomMetaTable) PostInit1() { } func (this *RawMetaTable[T]) Traverse(cb func(*T) bool) { for _, val := range this.rawList { if !cb(val) { break } } } func (this *RawMetaTable[T]) IsNoLoad() bool { return this.NoLoad } func (this *RawMetaTable[T]) PreInit1() { } func (this *RawMetaTable[T]) PostInit1() { } func (this *RawMetaTable[T]) RandElement() *T { if len(this.rawList) > 0 { return this.rawList[rand.Intn(len(this.rawList))] } return nil } func (this *RawMetaTable[T]) ElementsInit(round int) { type RoundInit1 interface { Init1() } type RoundInit2 interface { Init2() } type RoundInit3 interface { Init3() } this.Traverse(func(obj *T) bool { var x interface{} = obj switch round { case 0: if init, ok := x.(RoundInit1); ok { init.Init1() } case 1: if init, ok := x.(RoundInit2); ok { init.Init2() } case 2: if init, ok := x.(RoundInit3); ok { init.Init3() } } return true }) } func (this *IdMetaTable[T]) GetById(id int64) *T { if v, ok := this.idHash.Load(id); ok { return *v } else { return nil } } func (this *IdMetaTable[T]) RandElementExclude(cb func(*T) bool) *T { if len(this.rawList) > 0 { tryCount := 1 for tryCount < 1000 { meta := this.rawList[rand.Intn(len(this.rawList))] if cb(meta) { return meta } } } return nil } func (this *RawMetaTable[T]) Load() { if this.NoLoad { return } if jsonStr, err := ReadJsonFile(this.FileName); err == nil { switch q5.JsonStrType(jsonStr) { case q5.JSON_ARRAY: break case q5.JSON_OBJECT: jsonStr = "[" + jsonStr + "]" default: panic(fmt.Sprintf("error json format %s", this.FileName)) } var rows []map[string]interface{} err1 := json.Unmarshal([]byte(jsonStr), &rows) if err1 != nil { panic(fmt.Sprintf("load metafile json decode aerror %s %s", this.FileName, err1)) } for _, row := range rows { var obj = new(T) var x interface{} = obj if loader, ok := x.(LoadFromKeyValue); ok { loader.LoadFromKv(row) } this.rawList = append(this.rawList, obj) } } else { panic(fmt.Sprintf("load metafile error %s %s", this.FileName, err)) } } func (this *IdMetaTable[T]) Load() { this.RawMetaTable.Load() this.idHash = new(q5.ConcurrentMap[int64, *T]) i := int64(0) getFuncName := "Get" + q5.ConvertUpperCamelCase(this.PrimKey) this.Traverse(func(obj *T) bool { if this.PrimKey == "" { this.idHash.Store(i, obj) } else { in := []reflect.Value{} method := reflect.ValueOf(obj).MethodByName(getFuncName) out := method.Call(in) if key, err := q5.ToInt64Ex(out[0].Interface()); err == nil { this.idHash.Store(key, obj) } else { panic("IdMetaTable load PrimKey error") } } i++ return true }) } func (this *NameMetaTable[T]) GetByName(name string) *T { if v, ok := this.nameHash.Load(name); ok { return *v } else { return nil } } func (this *NameMetaTable[T]) Load() { this.RawMetaTable.Load() this.nameHash = new(q5.ConcurrentMap[string, *T]) i := int64(0) getFuncName := "Get" + q5.ConvertUpperCamelCase(this.PrimKey) this.Traverse(func(obj *T) bool { in := []reflect.Value{} method := reflect.ValueOf(obj).MethodByName(getFuncName) out := method.Call(in) if key, err := q5.ToStringEx(out[0].Interface()); err == nil { this.nameHash.Store(key, obj) } else { panic("NameMetaTable load PrimKey error") } i++ return true }) } func ReadMetaTableField[T string | int | int32 | int64 | float32 | float64]( fieldPtr *T, fieldName string, flags *uint64, flagIdx uint64, kv map[string]interface{}) { if val, ok := kv[fieldName]; ok { if !q5.DuckToSimple(val, fieldPtr) { panic("ReadMetaTableField error") } *flags |= uint64(1) << flagIdx } } func LoadMetaTable(table interface{}) { ele := reflect.ValueOf(table).Elem() for i := 0; i < ele.NumField(); i++ { var tbl MetaTable = ele.Field(i).Interface().(MetaTable) if !tbl.IsNoLoad() { tbl.PreInit1() } } for i := 0; i < ele.NumField(); i++ { var tbl MetaTable = ele.Field(i).Interface().(MetaTable) if !tbl.IsNoLoad() { tbl.Load() } } for i := 0; i < 3; i++ { for ii := 0; ii < ele.NumField(); ii++ { var tbl MetaTable = ele.Field(ii).Interface().(MetaTable) tbl.ElementsInit(i) } } for i := 0; i < ele.NumField(); i++ { var tbl MetaTable = ele.Field(i).Interface().(MetaTable) if !tbl.IsNoLoad() { tbl.PostInit1() } } }