github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/pkg/sqlbuilder/utils_field.go (about) 1 package sqlbuilder 2 3 import ( 4 "context" 5 "database/sql/driver" 6 "fmt" 7 "go/ast" 8 "reflect" 9 "strings" 10 "sync" 11 12 reflectx "github.com/octohelm/x/reflect" 13 typesx "github.com/octohelm/x/types" 14 ) 15 16 func StructFieldsFor(ctx context.Context, typ typesx.Type) []*StructField { 17 return defaultStructFieldsFactory.TableFieldsFor(ctx, typ) 18 } 19 20 var defaultStructFieldsFactory = &StructFieldsFactory{} 21 22 type StructFieldsFactory struct { 23 cache sync.Map 24 } 25 26 func (tf *StructFieldsFactory) TableFieldsFor(ctx context.Context, typ typesx.Type) []*StructField { 27 typ = typesx.Deref(typ) 28 29 underlying := typ.Unwrap() 30 31 if v, ok := tf.cache.Load(underlying); ok { 32 return v.([]*StructField) 33 } 34 35 tfs := make([]*StructField, 0) 36 37 EachStructField(ctx, typ, func(f *StructField) bool { 38 tagDB := f.Tags["db"] 39 if tagDB != "" && tagDB != "-" { 40 tfs = append(tfs, f) 41 } 42 return true 43 }) 44 45 tf.cache.Store(underlying, tfs) 46 47 return tfs 48 } 49 50 var typeModel = reflect.TypeOf((*Model)(nil)).Elem() 51 var driverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem() 52 53 func EachStructField(ctx context.Context, tpe typesx.Type, each func(p *StructField) bool) { 54 if tpe.Kind() != reflect.Struct { 55 panic(fmt.Errorf("model %s must be a struct", tpe.Name())) 56 } 57 (&fieldWalker{}).walk(ctx, tpe, each) 58 } 59 60 type fieldWalker struct { 61 loc []int 62 modelLoc []int 63 modelType typesx.Type 64 } 65 66 func (w *fieldWalker) walk(ctx context.Context, tpe typesx.Type, each func(p *StructField) bool) { 67 modelLoc := w.modelLoc[:] 68 modelType := w.modelType 69 70 if ok := tpe.Implements(typesx.FromRType(typeModel)); ok { 71 if modelType != nil && modelType.NumField() == 1 && modelType.Field(0).Anonymous() { 72 // extendable 73 74 } else { 75 modelType = tpe 76 modelLoc = w.loc[:] 77 } 78 } 79 80 for i := 0; i < tpe.NumField(); i++ { 81 f := tpe.Field(i) 82 83 if !ast.IsExported(f.Name()) { 84 continue 85 } 86 87 loc := append(w.loc, i) 88 89 tags := reflectx.ParseStructTags(string(f.Tag())) 90 displayName := f.Name() 91 92 tagDB, hasDB := tags["db"] 93 if hasDB { 94 if name := tagDB.Name(); name == "-" { 95 // skip name:"-" 96 continue 97 } else { 98 if name != "" { 99 displayName = name 100 } 101 } 102 } 103 104 if (f.Anonymous() || f.Type().Name() == f.Name()) && (!hasDB) { 105 fieldType := f.Type() 106 107 if !fieldType.Implements(typesx.FromRType(driverValuer)) { 108 for fieldType.Kind() == reflect.Ptr { 109 fieldType = fieldType.Elem() 110 } 111 112 if fieldType.Kind() == reflect.Struct { 113 (&fieldWalker{ 114 loc: loc, 115 modelType: modelType, 116 modelLoc: modelLoc, 117 }).walk(ctx, fieldType, each) 118 continue 119 } 120 } 121 } 122 123 p := &StructField{} 124 p.FieldName = f.Name() 125 p.Type = f.Type() 126 p.Field = f 127 p.Tags = tags 128 p.Name = strings.ToLower(displayName) 129 130 p.Loc = make([]int, len(loc)) 131 copy(p.Loc, loc) 132 133 p.ModelLoc = make([]int, len(modelLoc)) 134 copy(p.ModelLoc, modelLoc) 135 136 p.ColumnType = *ColumnDefFromTypeAndTag(p.Type, string(tagDB)) 137 138 if !each(p) { 139 break 140 } 141 } 142 } 143 144 type StructField struct { 145 Name string 146 FieldName string 147 Type typesx.Type 148 Field typesx.StructField 149 Tags map[string]reflectx.StructTag 150 Loc []int 151 ModelLoc []int 152 ColumnType ColumnDef 153 } 154 155 func fieldValue(structReflectValue reflect.Value, locs []int) reflect.Value { 156 n := len(locs) 157 158 if n == 0 { 159 return structReflectValue 160 } 161 162 if n < 0 { 163 return reflect.Value{} 164 } 165 166 structReflectValue = reflectx.Indirect(structReflectValue) 167 168 fv := structReflectValue 169 170 for i := 0; i < n; i++ { 171 loc := locs[i] 172 fv = fv.Field(loc) 173 174 // last loc should keep ptr value 175 if i < n-1 { 176 for fv.Kind() == reflect.Ptr { 177 // notice the ptr struct ensure only for Ptr Anonymous StructField 178 if fv.IsNil() { 179 fv.Set(reflectx.New(fv.Type())) 180 } 181 fv = fv.Elem() 182 } 183 } 184 } 185 186 return fv 187 } 188 189 func (p *StructField) FieldValue(structReflectValue reflect.Value) reflect.Value { 190 return fieldValue(structReflectValue, p.Loc) 191 } 192 193 func (p *StructField) FieldModelValue(structReflectValue reflect.Value) reflect.Value { 194 return fieldValue(structReflectValue, p.ModelLoc) 195 }