github.com/searKing/golang/go@v1.2.117/reflect/structinfo/structinfo.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package structinfo 6 7 import ( 8 "errors" 9 "fmt" 10 "reflect" 11 "strings" 12 "sync" 13 14 "github.com/searKing/golang/go/container/lru" 15 ) 16 17 // -------------------------------------------------------------------------- 18 // Maintain a mapping of keys to structure field indexes 19 20 type StructInfo struct { 21 fieldsLRU lru.LRU 22 Zero reflect.Value 23 } 24 type fieldInfo struct { 25 Key string 26 Num int 27 Tags map[string]string 28 } 29 30 var structMap = make(map[reflect.Type]*StructInfo) 31 var structMapMutex sync.RWMutex 32 33 type externalPanic string 34 35 func (e externalPanic) String() string { 36 return string(e) 37 } 38 func IsStructFieldPrivate(field reflect.StructField) bool { 39 if field.PkgPath != "" && !field.Anonymous { 40 return true // Private field 41 } 42 return false 43 } 44 45 // Single Instance 46 // Ignore the private field 47 func GetStructInfo(st reflect.Type) (*StructInfo, error) { 48 // lazyFound 49 structMapMutex.RLock() 50 sinfo, found := structMap[st] 51 structMapMutex.RUnlock() 52 if found { 53 return sinfo, nil 54 } 55 56 // Traversal all Fields of the Struct 57 n := st.NumField() 58 fieldsLRU := lru.LRU{} 59 inlineMap := -1 60 for i := 0; i != n; i++ { 61 field := st.Field(i) 62 if IsStructFieldPrivate(field) { 63 continue // Private field 64 } 65 66 info := fieldInfo{Num: i} 67 68 tag := field.Tag.Get("bson") 69 if tag == "" && strings.Index(string(field.Tag), ":") < 0 { 70 tag = string(field.Tag) 71 } 72 if tag == "-" { 73 continue 74 } 75 76 inline := false 77 fields := strings.Split(tag, ",") 78 if len(fields) > 1 { 79 for _, flag := range fields[1:] { 80 switch flag { 81 case "omitempty": 82 info.Tags["OmitEmpty"] = "omitempty" 83 case "inline": 84 inline = true 85 default: 86 msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st) 87 panic(externalPanic(msg)) 88 } 89 } 90 tag = fields[0] 91 } 92 93 if inline { 94 switch field.Type.Kind() { 95 case reflect.Map: 96 if inlineMap >= 0 { 97 return nil, errors.New("Multiple ,inline maps in struct " + st.String()) 98 } 99 if field.Type.Key() != reflect.TypeOf("") { 100 return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) 101 } 102 inlineMap = info.Num 103 case reflect.Struct: 104 sinfo, err := GetStructInfo(field.Type) 105 if err != nil { 106 return nil, err 107 } 108 109 for _, finfoPair := range sinfo.fieldsLRU.Pairs() { 110 if _, found := fieldsLRU.Find(finfoPair.Key); found { 111 msg := "Duplicated key '" + finfoPair.Key.(string) + "' in struct " + st.String() 112 return nil, errors.New(msg) 113 } 114 fieldsLRU.AddPair(finfoPair) 115 } 116 default: 117 panic("Option ,inline needs a struct value or map field") 118 } 119 continue 120 } 121 122 if tag != "" { 123 info.Key = tag 124 } else { 125 info.Key = strings.ToLower(field.Name) 126 } 127 128 if _, found = fieldsLRU.Find(info.Key); found { 129 msg := "Duplicated key '" + info.Key + "' in struct " + st.String() 130 return nil, errors.New(msg) 131 } 132 fieldsLRU.Add(info.Key, info) 133 } 134 sinfo = &StructInfo{ 135 fieldsLRU, 136 reflect.New(st).Elem(), 137 } 138 structMapMutex.Lock() 139 structMap[st] = sinfo 140 structMapMutex.Unlock() 141 return sinfo, nil 142 }