github.com/gogf/gf/v2@v2.7.4/util/gconv/internal/structcache/structcache_cached_struct_info.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package structcache 8 9 import ( 10 "reflect" 11 "strings" 12 "time" 13 14 "github.com/gogf/gf/v2/internal/utils" 15 "github.com/gogf/gf/v2/os/gtime" 16 ) 17 18 // CachedStructInfo holds the cached info for certain struct. 19 type CachedStructInfo struct { 20 // This map field is mainly used in the bindStructWithLoopParamsMap method 21 // key = field's name 22 // Will save all field names and PriorityTagAndFieldName 23 // for example: 24 // field string `json:"name"` 25 // 26 // It will be stored twice, which keys are `name` and `field`. 27 tagOrFiledNameToFieldInfoMap map[string]*CachedFieldInfo 28 29 // All sub attributes field info slice. 30 FieldConvertInfos []*CachedFieldInfo 31 } 32 33 func (csi *CachedStructInfo) HasNoFields() bool { 34 return len(csi.tagOrFiledNameToFieldInfoMap) == 0 35 } 36 37 func (csi *CachedStructInfo) GetFieldInfo(fieldName string) *CachedFieldInfo { 38 return csi.tagOrFiledNameToFieldInfoMap[fieldName] 39 } 40 41 func (csi *CachedStructInfo) AddField(field reflect.StructField, fieldIndexes []int, priorityTags []string) { 42 alreadyExistFieldInfo, ok := csi.tagOrFiledNameToFieldInfoMap[field.Name] 43 if !ok { 44 cachedFieldInfo := csi.makeCachedFieldInfo(field, fieldIndexes, priorityTags) 45 for _, tagOrFieldName := range cachedFieldInfo.PriorityTagAndFieldName { 46 newFieldInfo := &CachedFieldInfo{ 47 CachedFieldInfoBase: cachedFieldInfo.CachedFieldInfoBase, 48 IsField: tagOrFieldName == field.Name, 49 } 50 csi.tagOrFiledNameToFieldInfoMap[tagOrFieldName] = newFieldInfo 51 if newFieldInfo.IsField { 52 csi.FieldConvertInfos = append(csi.FieldConvertInfos, newFieldInfo) 53 } 54 } 55 return 56 } 57 // If the field name and type are the same 58 if alreadyExistFieldInfo.StructField.Type == field.Type { 59 alreadyExistFieldInfo.OtherSameNameField = append( 60 alreadyExistFieldInfo.OtherSameNameField, 61 csi.copyCachedInfoWithFieldIndexes(alreadyExistFieldInfo, fieldIndexes), 62 ) 63 return 64 } 65 // If the types are different, some information needs to be reset 66 alreadyExistFieldInfo.OtherSameNameField = append( 67 alreadyExistFieldInfo.OtherSameNameField, 68 csi.makeCachedFieldInfo(field, fieldIndexes, priorityTags), 69 ) 70 } 71 72 // copyCachedInfoWithFieldIndexes copies and returns a new CachedFieldInfo based on given CachedFieldInfo, but different 73 // FieldIndexes. Mainly used for copying fields with the same name and type. 74 func (csi *CachedStructInfo) copyCachedInfoWithFieldIndexes(cfi *CachedFieldInfo, fieldIndexes []int) *CachedFieldInfo { 75 base := CachedFieldInfoBase{} 76 base = *cfi.CachedFieldInfoBase 77 base.FieldIndexes = fieldIndexes 78 return &CachedFieldInfo{ 79 CachedFieldInfoBase: &base, 80 } 81 } 82 83 func (csi *CachedStructInfo) makeCachedFieldInfo( 84 field reflect.StructField, fieldIndexes []int, priorityTags []string, 85 ) *CachedFieldInfo { 86 base := &CachedFieldInfoBase{ 87 IsCommonInterface: checkTypeIsCommonInterface(field), 88 StructField: field, 89 FieldIndexes: fieldIndexes, 90 ConvertFunc: csi.genFieldConvertFunc(field.Type.String()), 91 IsCustomConvert: csi.checkTypeHasCustomConvert(field.Type), 92 PriorityTagAndFieldName: csi.genPriorityTagAndFieldName(field, priorityTags), 93 RemoveSymbolsFieldName: utils.RemoveSymbols(field.Name), 94 } 95 base.LastFuzzyKey.Store(field.Name) 96 return &CachedFieldInfo{ 97 CachedFieldInfoBase: base, 98 } 99 } 100 101 func (csi *CachedStructInfo) genFieldConvertFunc(fieldType string) (convertFunc func(from any, to reflect.Value)) { 102 if fieldType[0] == '*' { 103 convertFunc = csi.genFieldConvertFunc(fieldType[1:]) 104 if convertFunc == nil { 105 return nil 106 } 107 return csi.genPtrConvertFunc(convertFunc) 108 } 109 switch fieldType { 110 case "int", "int8", "int16", "int32", "int64": 111 convertFunc = func(from any, to reflect.Value) { 112 to.SetInt(localCommonConverter.Int64(from)) 113 } 114 case "uint", "uint8", "uint16", "uint32", "uint64": 115 convertFunc = func(from any, to reflect.Value) { 116 to.SetUint(localCommonConverter.Uint64(from)) 117 } 118 case "string": 119 convertFunc = func(from any, to reflect.Value) { 120 to.SetString(localCommonConverter.String(from)) 121 } 122 case "float32": 123 convertFunc = func(from any, to reflect.Value) { 124 to.SetFloat(float64(localCommonConverter.Float32(from))) 125 } 126 case "float64": 127 convertFunc = func(from any, to reflect.Value) { 128 to.SetFloat(localCommonConverter.Float64(from)) 129 } 130 case "Time", "time.Time": 131 convertFunc = func(from any, to reflect.Value) { 132 *to.Addr().Interface().(*time.Time) = localCommonConverter.Time(from) 133 } 134 case "GTime", "gtime.Time": 135 convertFunc = func(from any, to reflect.Value) { 136 v := localCommonConverter.GTime(from) 137 if v == nil { 138 v = gtime.New() 139 } 140 *to.Addr().Interface().(*gtime.Time) = *v 141 } 142 case "bool": 143 convertFunc = func(from any, to reflect.Value) { 144 to.SetBool(localCommonConverter.Bool(from)) 145 } 146 case "[]byte": 147 convertFunc = func(from any, to reflect.Value) { 148 to.SetBytes(localCommonConverter.Bytes(from)) 149 } 150 default: 151 return nil 152 } 153 return convertFunc 154 } 155 156 func (csi *CachedStructInfo) genPriorityTagAndFieldName( 157 field reflect.StructField, priorityTags []string, 158 ) (priorityTagAndFieldName []string) { 159 for _, tag := range priorityTags { 160 value, ok := field.Tag.Lookup(tag) 161 if ok { 162 // If there's something else in the tag string, 163 // it uses the first part which is split using char ','. 164 // Example: 165 // orm:"id, priority" 166 // orm:"name, with:uid=id" 167 tagValueItems := strings.Split(value, ",") 168 // json:",omitempty" 169 trimmedTagName := strings.TrimSpace(tagValueItems[0]) 170 if trimmedTagName != "" { 171 priorityTagAndFieldName = append(priorityTagAndFieldName, trimmedTagName) 172 break 173 } 174 } 175 } 176 priorityTagAndFieldName = append(priorityTagAndFieldName, field.Name) 177 return 178 } 179 180 func (csi *CachedStructInfo) checkTypeHasCustomConvert(fieldType reflect.Type) bool { 181 if fieldType.Kind() == reflect.Ptr { 182 fieldType = fieldType.Elem() 183 } 184 _, ok := customConvertTypeMap[fieldType] 185 return ok 186 } 187 188 func (csi *CachedStructInfo) genPtrConvertFunc( 189 convertFunc func(from any, to reflect.Value), 190 ) func(from any, to reflect.Value) { 191 return func(from any, to reflect.Value) { 192 if to.IsNil() { 193 to.Set(reflect.New(to.Type().Elem())) 194 } 195 convertFunc(from, to.Elem()) 196 } 197 }