github.com/gogf/gf@v1.16.9/internal/structs/structs_tag.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 structs 8 9 import ( 10 "errors" 11 "reflect" 12 "strconv" 13 ) 14 15 // ParseTag parses tag string into map. 16 func ParseTag(tag string) map[string]string { 17 var ( 18 key string 19 data = make(map[string]string) 20 ) 21 for tag != "" { 22 // Skip leading space. 23 i := 0 24 for i < len(tag) && tag[i] == ' ' { 25 i++ 26 } 27 tag = tag[i:] 28 if tag == "" { 29 break 30 } 31 // Scan to colon. A space, a quote or a control character is a syntax error. 32 // Strictly speaking, control chars include the range [0x7f, 0x9f], not just 33 // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters 34 // as it is simpler to inspect the tag's bytes than the tag's runes. 35 i = 0 36 for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { 37 i++ 38 } 39 if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { 40 break 41 } 42 key = tag[:i] 43 tag = tag[i+1:] 44 45 // Scan quoted string to find value. 46 i = 1 47 for i < len(tag) && tag[i] != '"' { 48 if tag[i] == '\\' { 49 i++ 50 } 51 i++ 52 } 53 if i >= len(tag) { 54 break 55 } 56 quotedValue := tag[:i+1] 57 tag = tag[i+1:] 58 value, err := strconv.Unquote(quotedValue) 59 if err != nil { 60 panic(err) 61 } 62 data[key] = value 63 } 64 return data 65 } 66 67 // TagFields retrieves and returns struct tags as []*Field from `pointer`. 68 // 69 // The parameter `pointer` should be type of struct/*struct. 70 // 71 // Note that it only retrieves the exported attributes with first letter up-case from struct. 72 func TagFields(pointer interface{}, priority []string) ([]*Field, error) { 73 return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{}) 74 } 75 76 // TagMapName retrieves and returns struct tags as map[tag]attribute from `pointer`. 77 // 78 // The parameter `pointer` should be type of struct/*struct. 79 // 80 // Note that it only retrieves the exported attributes with first letter up-case from struct. 81 func TagMapName(pointer interface{}, priority []string) (map[string]string, error) { 82 fields, err := TagFields(pointer, priority) 83 if err != nil { 84 return nil, err 85 } 86 tagMap := make(map[string]string, len(fields)) 87 for _, field := range fields { 88 tagMap[field.TagValue] = field.Name() 89 } 90 return tagMap, nil 91 } 92 93 // TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it. 94 // The parameter `object` should be either type of struct/*struct/[]struct/[]*struct. 95 // 96 // Note that it only retrieves the exported attributes with first letter up-case from struct. 97 func TagMapField(object interface{}, priority []string) (map[string]*Field, error) { 98 fields, err := TagFields(object, priority) 99 if err != nil { 100 return nil, err 101 } 102 tagMap := make(map[string]*Field, len(fields)) 103 for _, field := range fields { 104 tagField := field 105 tagMap[field.TagValue] = tagField 106 } 107 return tagMap, nil 108 } 109 110 func getFieldValues(value interface{}) ([]*Field, error) { 111 var ( 112 reflectValue reflect.Value 113 reflectKind reflect.Kind 114 ) 115 if v, ok := value.(reflect.Value); ok { 116 reflectValue = v 117 reflectKind = reflectValue.Kind() 118 } else { 119 reflectValue = reflect.ValueOf(value) 120 reflectKind = reflectValue.Kind() 121 } 122 for { 123 switch reflectKind { 124 case reflect.Ptr: 125 if !reflectValue.IsValid() || reflectValue.IsNil() { 126 // If pointer is type of *struct and nil, then automatically create a temporary struct. 127 reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() 128 reflectKind = reflectValue.Kind() 129 } else { 130 reflectValue = reflectValue.Elem() 131 reflectKind = reflectValue.Kind() 132 } 133 case reflect.Array, reflect.Slice: 134 reflectValue = reflect.New(reflectValue.Type().Elem()).Elem() 135 reflectKind = reflectValue.Kind() 136 default: 137 goto exitLoop 138 } 139 } 140 141 exitLoop: 142 for reflectKind == reflect.Ptr { 143 reflectValue = reflectValue.Elem() 144 reflectKind = reflectValue.Kind() 145 } 146 if reflectKind != reflect.Struct { 147 return nil, errors.New("given value should be either type of struct/*struct/[]struct/[]*struct") 148 } 149 var ( 150 structType = reflectValue.Type() 151 length = reflectValue.NumField() 152 fields = make([]*Field, length) 153 ) 154 for i := 0; i < length; i++ { 155 fields[i] = &Field{ 156 Value: reflectValue.Field(i), 157 Field: structType.Field(i), 158 } 159 } 160 return fields, nil 161 } 162 163 func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap map[string]struct{}) ([]*Field, error) { 164 fields, err := getFieldValues(pointer) 165 if err != nil { 166 return nil, err 167 } 168 var ( 169 tagValue = "" 170 tagFields = make([]*Field, 0) 171 ) 172 for _, field := range fields { 173 // Only retrieve exported attributes. 174 if !field.IsExported() { 175 continue 176 } 177 tagValue = "" 178 for _, p := range priority { 179 tagValue = field.Tag(p) 180 if tagValue != "" && tagValue != "-" { 181 break 182 } 183 } 184 if tagValue != "" { 185 // Filter repeated tag. 186 if _, ok := tagMap[tagValue]; ok { 187 continue 188 } 189 tagField := field 190 tagField.TagValue = tagValue 191 tagFields = append(tagFields, tagField) 192 } 193 // If this is an embedded attribute, it retrieves the tags recursively. 194 if field.IsEmbedded() { 195 if subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, tagMap); err != nil { 196 return nil, err 197 } else { 198 tagFields = append(tagFields, subTagFields...) 199 } 200 } 201 } 202 return tagFields, nil 203 }