github.com/kunlun-qilian/sqlx/v3@v3.0.0/builder/utils_field.go (about) 1 package builder 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/go-courier/x/reflect" 13 typesx "github.com/go-courier/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 58 var walk func(tpe typesx.Type, modelLoc []int, parents ...int) 59 60 walk = func(tpe typesx.Type, modelLoc []int, parents ...int) { 61 if ok := tpe.Implements(typesx.FromRType(typeModel)); ok { 62 modelLoc = parents 63 } 64 65 for i := 0; i < tpe.NumField(); i++ { 66 f := tpe.Field(i) 67 68 if !ast.IsExported(f.Name()) { 69 continue 70 } 71 72 loc := append(parents, i) 73 74 tags := reflectx.ParseStructTags(string(f.Tag())) 75 76 displayName := f.Name() 77 78 tagDB, hasDB := tags["db"] 79 if hasDB { 80 if name := tagDB.Name(); name == "-" { 81 // skip name:"-" 82 continue 83 } else { 84 if name != "" { 85 displayName = name 86 } 87 } 88 } 89 90 if (f.Anonymous() || f.Type().Name() == f.Name()) && (!hasDB) { 91 fieldType := f.Type() 92 93 if !fieldType.Implements(typesx.FromRType(driverValuer)) { 94 for fieldType.Kind() == reflect.Ptr { 95 fieldType = fieldType.Elem() 96 } 97 98 if fieldType.Kind() == reflect.Struct { 99 walk(fieldType, modelLoc, loc...) 100 continue 101 } 102 } 103 } 104 105 p := &StructField{} 106 p.FieldName = f.Name() 107 p.Type = f.Type() 108 p.Field = f 109 p.Tags = tags 110 p.Name = strings.ToLower(displayName) 111 112 p.Loc = make([]int, len(loc)) 113 copy(p.Loc, loc) 114 115 p.ModelLoc = make([]int, len(modelLoc)) 116 copy(p.ModelLoc, modelLoc) 117 118 p.ColumnType = *ColumnTypeFromTypeAndTag(p.Type, string(tagDB)) 119 120 if !each(p) { 121 break 122 } 123 } 124 } 125 126 walk(tpe, []int{}) 127 } 128 129 type StructField struct { 130 Name string 131 FieldName string 132 Type typesx.Type 133 Field typesx.StructField 134 Tags map[string]reflectx.StructTag 135 Loc []int 136 ModelLoc []int 137 ColumnType ColumnType 138 } 139 140 func fieldValue(structReflectValue reflect.Value, locs []int) reflect.Value { 141 n := len(locs) 142 143 if n == 0 { 144 return structReflectValue 145 } 146 147 if n < 0 { 148 return reflect.Value{} 149 } 150 151 structReflectValue = reflectx.Indirect(structReflectValue) 152 153 fv := structReflectValue 154 155 for i := 0; i < n; i++ { 156 loc := locs[i] 157 fv = fv.Field(loc) 158 159 // last loc should keep ptr value 160 if i < n-1 { 161 for fv.Kind() == reflect.Ptr { 162 // notice the ptr struct ensure only for Ptr Anonymous StructField 163 if fv.IsNil() { 164 fv.Set(reflectx.New(fv.Type())) 165 } 166 fv = fv.Elem() 167 } 168 } 169 } 170 171 return fv 172 } 173 174 func (p *StructField) FieldValue(structReflectValue reflect.Value) reflect.Value { 175 return fieldValue(structReflectValue, p.Loc) 176 } 177 178 func (p *StructField) FieldModelValue(structReflectValue reflect.Value) reflect.Value { 179 return fieldValue(structReflectValue, p.ModelLoc) 180 }