github.com/gogf/gf/v2@v2.7.4/util/gconv/internal/structcache/structcache_cached_field_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 "sync/atomic" 12 ) 13 14 // CachedFieldInfo holds the cached info for struct field. 15 type CachedFieldInfo struct { 16 // WARN: 17 // The [CachedFieldInfoBase] structure cannot be merged with the following [IsField] field into one structure. 18 // The [IsField] field should be used separately in the [bindStructWithLoopParamsMap] method 19 *CachedFieldInfoBase 20 21 // This field is mainly used in the [bindStructWithLoopParamsMap] method. 22 // This field is needed when both `fieldName` and `tag` of a field exist in the map. 23 // For example: 24 // field string `json:"name"` 25 // map = { 26 // "field" : "f1", 27 // "name" : "n1", 28 // } 29 // The `name` should be used here. 30 // In the bindStructWithLoopParamsMap method, due to the disorder of `map`, `field` may be traversed first. 31 // This field is more about priority, that is, the priority of `name` is higher than that of `field`, 32 // even if it has been set before. 33 IsField bool 34 } 35 36 // CachedFieldInfoBase holds the cached info for struct field. 37 type CachedFieldInfoBase struct { 38 // FieldIndexes holds the global index number from struct info. 39 // The field may belong to an embedded structure, so it is defined here as []int. 40 FieldIndexes []int 41 42 // PriorityTagAndFieldName holds the tag value(conv, param, p, c, json) and the field name. 43 // PriorityTagAndFieldName contains the field name, which is the last item of slice. 44 PriorityTagAndFieldName []string 45 46 // IsCommonInterface marks this field implements common interfaces as: 47 // - iUnmarshalValue 48 // - iUnmarshalText 49 // - iUnmarshalJSON 50 // Purpose: reduce the interface asserting cost in runtime. 51 IsCommonInterface bool 52 53 // IsCustomConvert marks there custom converting function for this field type. 54 IsCustomConvert bool 55 56 // StructField is the type info of this field. 57 StructField reflect.StructField 58 59 // OtherSameNameField stores fields with the same name and type or different types of nested structures. 60 // 61 // For example: 62 // type ID struct{ 63 // ID1 string 64 // ID2 int 65 // } 66 // type Card struct{ 67 // ID 68 // ID1 uint64 69 // ID2 int64 70 // } 71 // 72 // We will cache each ID1 and ID2 separately, 73 // even if their types are different and their indexes are different 74 OtherSameNameField []*CachedFieldInfo 75 76 // ConvertFunc is the converting function for this field. 77 ConvertFunc func(from any, to reflect.Value) 78 79 // The last fuzzy matching key for this field. 80 // The fuzzy matching occurs only if there are no direct tag and field name matching in the params map. 81 // TODO If different paramsMaps contain paramKeys in different formats and all hit the same fieldName, 82 // the cached value may be continuously updated. 83 // LastFuzzyKey string. 84 LastFuzzyKey atomic.Value 85 86 // removeSymbolsFieldName is used for quick fuzzy match for parameter key. 87 // removeSymbolsFieldName = utils.RemoveSymbols(fieldName) 88 RemoveSymbolsFieldName string 89 } 90 91 // FieldName returns the field name of current field info. 92 func (cfi *CachedFieldInfo) FieldName() string { 93 return cfi.PriorityTagAndFieldName[len(cfi.PriorityTagAndFieldName)-1] 94 } 95 96 // GetFieldReflectValueFrom retrieves and returns the `reflect.Value` of given struct field, 97 // which is used for directly value assignment. 98 // 99 // Note that, the input parameter `structValue` might be initialized internally. 100 func (cfi *CachedFieldInfo) GetFieldReflectValueFrom(structValue reflect.Value) reflect.Value { 101 if len(cfi.FieldIndexes) == 1 { 102 // no nested struct. 103 return structValue.Field(cfi.FieldIndexes[0]) 104 } 105 return cfi.fieldReflectValue(structValue, cfi.FieldIndexes) 106 } 107 108 // GetOtherFieldReflectValueFrom retrieves and returns the `reflect.Value` of given struct field with nested index 109 // by `fieldLevel`, which is used for directly value assignment. 110 // 111 // Note that, the input parameter `structValue` might be initialized internally. 112 func (cfi *CachedFieldInfo) GetOtherFieldReflectValueFrom(structValue reflect.Value, fieldIndex []int) reflect.Value { 113 if len(fieldIndex) == 1 { 114 // no nested struct. 115 return structValue.Field(fieldIndex[0]) 116 } 117 return cfi.fieldReflectValue(structValue, fieldIndex) 118 } 119 120 func (cfi *CachedFieldInfo) fieldReflectValue(v reflect.Value, fieldIndexes []int) reflect.Value { 121 for i, x := range fieldIndexes { 122 if i > 0 { 123 // it means nested struct. 124 switch v.Kind() { 125 case reflect.Pointer: 126 if v.IsNil() { 127 // Initialization. 128 v.Set(reflect.New(v.Type().Elem())) 129 } 130 v = v.Elem() 131 132 case reflect.Interface: 133 // Compatible with previous code 134 // Interface => struct 135 v = v.Elem() 136 if v.Kind() == reflect.Ptr { 137 // maybe *struct or other types 138 v = v.Elem() 139 } 140 default: 141 } 142 } 143 v = v.Field(x) 144 } 145 return v 146 }