github.com/RevenueMonster/sqlike@v1.0.6/reflext/reflext.go (about) 1 package reflext 2 3 import ( 4 "reflect" 5 "sort" 6 "strconv" 7 "strings" 8 ) 9 10 // Structer : 11 type Structer interface { 12 Fields() []StructFielder 13 Properties() []StructFielder 14 LookUpFieldByName(name string) (StructFielder, bool) 15 GetByTraversal(index []int) StructFielder 16 } 17 18 // StructFielder : 19 type StructFielder interface { 20 // New name of struct field 21 Name() string 22 23 // reflect.Type of the field 24 Type() reflect.Type 25 26 // index position of the field 27 Index() []int 28 29 Tag() StructTag 30 31 // if the field is struct, parent will not be nil 32 // this will be the parent struct of current struct 33 Parent() StructFielder 34 35 ParentByTraversal(cb func(StructFielder) bool) StructFielder 36 37 // if the field is struct, children will not be nil 38 // this will be the fields of current struct 39 Children() []StructFielder 40 41 // determine the field is nullable 42 IsNullable() bool 43 44 // determine the field is embedded struct 45 IsEmbedded() bool 46 } 47 48 // StructTag : 49 type StructTag struct { 50 originalName string 51 name string 52 opts map[string]string 53 } 54 55 // Name : 56 func (st StructTag) Name() string { 57 return st.name 58 } 59 60 // OriginalName : 61 func (st StructTag) OriginalName() string { 62 return st.originalName 63 } 64 65 // Get : 66 func (st StructTag) Get(key string) string { 67 return st.opts[key] 68 } 69 70 // LookUp : 71 func (st StructTag) LookUp(key string) (val string, exist bool) { 72 val, exist = st.opts[key] 73 return 74 } 75 76 // StructField : 77 type StructField struct { 78 id string 79 idx []int 80 name string 81 path string 82 t reflect.Type 83 null bool 84 tag StructTag 85 embed bool 86 parent StructFielder 87 children []StructFielder 88 } 89 90 var _ StructFielder = (*StructField)(nil) 91 92 // Name : 93 func (sf *StructField) Name() string { 94 return sf.path 95 } 96 97 // Type : 98 func (sf *StructField) Type() reflect.Type { 99 return sf.t 100 } 101 102 // Tag : 103 func (sf *StructField) Tag() StructTag { 104 return sf.tag 105 } 106 107 // Index : 108 func (sf *StructField) Index() []int { 109 return sf.idx 110 } 111 112 // Parent : 113 func (sf *StructField) Parent() StructFielder { 114 return sf.parent 115 } 116 117 // Children : 118 func (sf *StructField) Children() []StructFielder { 119 return sf.children 120 } 121 122 // IsNullable : 123 func (sf *StructField) IsNullable() bool { 124 return sf.null 125 } 126 127 // IsEmbedded : 128 func (sf *StructField) IsEmbedded() bool { 129 return sf.embed 130 } 131 132 // ParentByTraversal : 133 func (sf *StructField) ParentByTraversal(cb func(StructFielder) bool) StructFielder { 134 prnt := sf.parent 135 for prnt != nil { 136 if cb(prnt) { 137 break 138 } 139 prnt = prnt.Parent() 140 } 141 return prnt 142 } 143 144 // Struct : 145 type Struct struct { 146 tree StructFielder 147 fields Fields // all fields belong to this struct 148 properties Fields // available properties in sequence 149 indexes map[string]StructFielder 150 names map[string]StructFielder 151 } 152 153 var _ Structer = (*Struct)(nil) 154 155 // Fields : 156 func (s *Struct) Fields() []StructFielder { 157 return append(make(Fields, 0, len(s.fields)), s.fields...) 158 } 159 160 // Properties : 161 func (s *Struct) Properties() []StructFielder { 162 return append(make(Fields, 0, len(s.properties)), s.properties...) 163 } 164 165 // LookUpFieldByName : 166 func (s *Struct) LookUpFieldByName(name string) (StructFielder, bool) { 167 x, ok := s.names[name] 168 return x, ok 169 } 170 171 // GetByTraversal : 172 func (s *Struct) GetByTraversal(index []int) StructFielder { 173 if len(index) == 0 { 174 return nil 175 } 176 177 tree := s.tree 178 for _, i := range index { 179 children := tree.Children() 180 if i >= len(children) || children[i] == nil { 181 return nil 182 } 183 tree = children[i] 184 } 185 return tree 186 } 187 188 // Fields : 189 type Fields []StructFielder 190 191 func (x Fields) FindIndex(cb func(f StructFielder) bool) int { 192 for idx, f := range x { 193 if cb(f) { 194 return idx 195 } 196 } 197 return -1 198 } 199 200 func (x Fields) Len() int { return len(x) } 201 202 func (x Fields) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 203 204 func (x Fields) Less(i, j int) bool { 205 for k, xik := range x[i].Index() { 206 if k >= len(x[j].Index()) { 207 return false 208 } 209 if xik != x[j].Index()[k] { 210 return xik < x[j].Index()[k] 211 } 212 } 213 return len(x[i].Index()) < len(x[j].Index()) 214 } 215 216 type typeQueue struct { 217 t reflect.Type 218 sf *StructField 219 pp string // parent path 220 } 221 222 func getCodec(t reflect.Type, tagName string, fmtFunc FormatFunc) *Struct { 223 fields := make([]StructFielder, 0) 224 225 root := &StructField{} 226 queue := []typeQueue{} 227 queue = append(queue, typeQueue{Deref(t), root, ""}) 228 229 for len(queue) > 0 { 230 q := queue[0] 231 q.sf.children = make([]StructFielder, 0) 232 233 for i := 0; i < q.t.NumField(); i++ { 234 f := q.t.Field(i) 235 236 // skip unexported fields 237 if len(f.PkgPath) != 0 && !f.Anonymous { 238 continue 239 } 240 241 tag := parseTag(f, tagName, fmtFunc) 242 if tag.name == "-" { 243 continue 244 } 245 246 sf := &StructField{ 247 id: strings.TrimLeft(q.sf.id+"."+strconv.Itoa(i), "."), 248 name: f.Name, 249 path: tag.name, 250 null: q.sf.null || IsNullable(f.Type), 251 t: f.Type, 252 tag: tag, 253 children: make([]StructFielder, 0), 254 } 255 256 if len(q.sf.Index()) > 0 { 257 sf.parent = q.sf 258 } 259 260 if sf.path == "" { 261 sf.path = sf.tag.name 262 } 263 264 if q.pp != "" { 265 sf.path = q.pp + "." + sf.path 266 } 267 268 ft := Deref(f.Type) 269 q.sf.children = append(q.sf.children, sf) 270 sf.idx = appendSlice(q.sf.idx, i) 271 sf.embed = ft.Kind() == reflect.Struct && f.Anonymous 272 273 if ft.Kind() == reflect.Struct { 274 // check recursive, prevent infinite loop 275 if q.t == ft { 276 goto nextStep 277 } 278 279 // embedded struct 280 path := sf.path 281 if f.Anonymous { 282 if sf.tag.OriginalName() == "" { 283 path = q.pp 284 } 285 // queue = append(queue, typeQueue{ft, sf, path}) 286 } 287 288 queue = append(queue, typeQueue{ft, sf, path}) 289 } 290 291 nextStep: 292 fields = append(fields, sf) 293 } 294 295 queue = queue[1:] 296 } 297 298 codec := &Struct{ 299 tree: root, 300 fields: fields, 301 properties: make([]StructFielder, 0, len(fields)), 302 indexes: make(map[string]StructFielder), 303 names: make(map[string]StructFielder), 304 } 305 306 lname := "" 307 sort.Sort(codec.fields) 308 309 for _, sf := range codec.fields { 310 codec.indexes[sf.(*StructField).id] = sf 311 if sf.Name() != "" && !sf.IsEmbedded() { 312 lname = strings.ToLower(sf.Name()) 313 codec.names[sf.Name()] = sf 314 315 idx := codec.properties.FindIndex(func(each StructFielder) bool { 316 return strings.ToLower(each.Tag().name) == lname 317 }) 318 if idx > -1 { 319 // remove item in the slice if the field name is same (overriding embedded struct field) 320 codec.properties = append(codec.properties[:idx], codec.properties[idx+1:]...) 321 } 322 323 prnt := sf.ParentByTraversal(func(f StructFielder) bool { 324 return !f.IsEmbedded() 325 }) 326 if len(sf.Index()) > 1 && 327 sf.Parent() != nil && prnt != nil { 328 continue 329 } 330 331 // not nested embedded struct or embedded struct 332 codec.properties = append(codec.properties, sf) 333 } 334 } 335 336 return codec 337 } 338 339 func appendSlice(s []int, i int) []int { 340 x := make([]int, len(s)+1) 341 copy(x, s) 342 x[len(x)-1] = i 343 return x 344 } 345 346 func parseTag(f reflect.StructField, tagName string, fmtFunc FormatFunc) (st StructTag) { 347 parts := strings.Split(f.Tag.Get(tagName), ",") 348 name := strings.TrimSpace(parts[0]) 349 st.originalName = name 350 if name == "" { 351 name = f.Name 352 if fmtFunc != nil { 353 name = fmtFunc(name) 354 } 355 } 356 st.name = name 357 st.opts = make(map[string]string) 358 if len(parts) > 1 { 359 for _, opt := range parts[1:] { 360 opt = strings.TrimSpace(opt) 361 if strings.Contains(opt, "=") { 362 kv := strings.SplitN(opt, "=", 2) 363 k := strings.TrimSpace(strings.ToLower(kv[0])) 364 st.opts[k] = strings.TrimSpace(kv[1]) 365 continue 366 } 367 opt = strings.ToLower(opt) 368 st.opts[opt] = "" 369 } 370 } 371 return 372 }