github.com/viant/toolbox@v0.34.5/struct_helper.go (about) 1 package toolbox 2 3 import ( 4 "fmt" 5 "github.com/go-errors/errors" 6 "reflect" 7 "strings" 8 ) 9 10 const ( 11 fieldNameKey = "fieldName" 12 anonymousKey = "anonymous" 13 fieldIndexKey = "fieldIndex" 14 defaultKey = "default" 15 ) 16 17 var columnMapping = []string{"column", "dateLayout", "dateFormat", "autoincrement", "primaryKey", "sequence", "valueMap", defaultKey, anonymousKey} 18 19 //ScanStructFunc scan supplied struct methods 20 func ScanStructMethods(structOrItsType interface{}, depth int, handler func(method reflect.Method) error) error { 21 var scanned = make(map[reflect.Type]bool) 22 return scanStructMethods(structOrItsType, scanned, depth, handler) 23 } 24 25 func scanStructMethods(structOrItsType interface{}, scanned map[reflect.Type]bool, depth int, handler func(method reflect.Method) error) error { 26 if depth < 0 { 27 return nil 28 } 29 30 structValue, err := TryDiscoverValueByKind(reflect.ValueOf(structOrItsType), reflect.Struct) 31 if err != nil { 32 structValue := reflect.ValueOf(structOrItsType) 33 if !(structValue.Kind() == reflect.Interface) { 34 return err 35 } 36 } 37 38 structType := structValue.Type() 39 if _, hasScan := scanned[structType]; hasScan { 40 return nil 41 } 42 43 scanned[structType] = true 44 45 for i := 0; i < structValue.NumField(); i++ { 46 fieldType := structType.Field(i) 47 if isExported := fieldType.PkgPath == ""; !isExported { 48 continue 49 } 50 if !fieldType.Anonymous { 51 continue 52 } 53 if !IsStruct(fieldType) { 54 continue 55 } 56 if fieldStructType, err := TryDiscoverTypeByKind(fieldType, reflect.Struct); err == nil { 57 fieldStruct := reflect.New(fieldStructType).Interface() 58 if err = scanStructMethods(fieldStruct, scanned, depth-1, handler); err != nil { 59 return err 60 } 61 } 62 } 63 64 structPtr, err := TryDiscoverValueByKind(reflect.ValueOf(structOrItsType), reflect.Ptr) 65 if err != nil { 66 return err 67 } 68 69 structTypePtr := structPtr.Type() 70 for i := 0; i < structTypePtr.NumMethod(); i++ { 71 method := structTypePtr.Method(i) 72 if isExported := method.PkgPath == ""; !isExported { 73 continue 74 } 75 if err := handler(method); err != nil { 76 return err 77 } 78 } 79 return nil 80 } 81 82 //StructField represents a struct field 83 type StructField struct { 84 Owner reflect.Value 85 Value reflect.Value 86 Type reflect.StructField 87 } 88 89 var onUnexportedHandler = IgnoreUnexportedFields 90 91 //UnexportedFieldHandler represents unexported field handler 92 type UnexportedFieldHandler func(structField *StructField) bool 93 94 //Handler ignoring unexported fields 95 func IgnoreUnexportedFields(structField *StructField) bool { 96 return false 97 } 98 99 func SetUnexportedFieldHandler(handler UnexportedFieldHandler) error { 100 if handler == nil { 101 return errors.New("handler was nil") 102 } 103 onUnexportedHandler = handler 104 return nil 105 } 106 107 //ProcessStruct reads passed in struct fields and values to pass it to provided handler 108 func ProcessStruct(aStruct interface{}, handler func(fieldType reflect.StructField, field reflect.Value) error) error { 109 structValue, err := TryDiscoverValueByKind(reflect.ValueOf(aStruct), reflect.Struct) 110 if err != nil { 111 return err 112 } 113 structType := structValue.Type() 114 115 var fields = make(map[string]*StructField) 116 for i := 0; i < structType.NumField(); i++ { 117 fieldType := structType.Field(i) 118 if !fieldType.Anonymous { 119 continue 120 } 121 field := structValue.Field(i) 122 if !IsStruct(field) { 123 fields[fieldType.Name] = &StructField{Type: fieldType, Value: field, Owner: structValue} 124 continue 125 } 126 var aStruct interface{} 127 if fieldType.Type.Kind() == reflect.Ptr { 128 if field.IsNil() { 129 if !field.CanSet() { 130 continue 131 } 132 structValue.Field(i).Set(reflect.New(fieldType.Type.Elem())) 133 } 134 aStruct = field.Interface() 135 } else { 136 137 if field.CanAddr() { 138 aStruct = field.Addr().Interface() 139 } else if field.CanInterface() { 140 aStruct = field.Interface() 141 } else { 142 continue 143 } 144 } 145 if err := ProcessStruct(aStruct, func(fieldType reflect.StructField, field reflect.Value) error { 146 structField := &StructField{Type: fieldType, Value: field, Owner: field} 147 if field.CanAddr() { 148 structField.Owner = field.Addr() 149 } 150 fields[fieldType.Name] = structField 151 return nil 152 }); err != nil { 153 return err 154 } 155 } 156 157 for i := 0; i < structType.NumField(); i++ { 158 fieldType := structType.Field(i) 159 if fieldType.Anonymous { 160 continue 161 } 162 field := structValue.Field(i) 163 structField := &StructField{Owner: structValue, Type: fieldType, Value: field} 164 if isExported := fieldType.PkgPath == ""; !isExported { 165 if !onUnexportedHandler(structField) { 166 continue 167 } 168 } 169 fields[fieldType.Name] = &StructField{Owner: structValue, Type: fieldType, Value: field} 170 } 171 172 for _, field := range fields { 173 if err := handler(field.Type, field.Value); err != nil { 174 return err 175 } 176 } 177 return nil 178 } 179 180 //BuildTagMapping builds map keyed by mappedKeyTag tag value, and value is another map of keys where tag name is presents in the tags parameter. 181 func BuildTagMapping(structTemplatePointer interface{}, mappedKeyTag string, resultExclusionTag string, inheritKeyFromField bool, convertKeyToLowerCase bool, tags []string) map[string](map[string]string) { 182 reflectStructType := DiscoverTypeByKind(structTemplatePointer, reflect.Struct) 183 var result = make(map[string]map[string]string) 184 var anonymousMappings = make(map[string]map[string]string) 185 186 for i := 0; i < reflectStructType.NumField(); i++ { 187 var field reflect.StructField 188 field = reflectStructType.Field(i) 189 key := getTagValues(field, mappedKeyTag) 190 191 if field.Anonymous && key == "" { 192 var anonymousType = DereferenceType(field.Type) 193 if anonymousType.Kind() == reflect.Struct { 194 anonymousMapping := BuildTagMapping(reflect.New(anonymousType).Interface(), mappedKeyTag, resultExclusionTag, inheritKeyFromField, convertKeyToLowerCase, tags) 195 for k, v := range anonymousMapping { 196 anonymousMappings[k] = v 197 anonymousMappings[k][anonymousKey] = "true" 198 anonymousMappings[k][fieldIndexKey] = AsString(i) 199 } 200 } 201 202 continue 203 } 204 205 isTransient := strings.EqualFold(field.Tag.Get(resultExclusionTag), "true") 206 if isTransient { 207 continue 208 } 209 210 if key == "" { 211 if !inheritKeyFromField { 212 continue 213 } 214 key = field.Name 215 } 216 217 if convertKeyToLowerCase { 218 key = strings.ToLower(key) 219 } 220 221 result[key] = make(map[string]string) 222 for _, tag := range tags { 223 tagValue := field.Tag.Get(tag) 224 if len(tagValue) > 0 { 225 result[key][tag] = tagValue 226 } 227 } 228 result[key][fieldNameKey] = field.Name 229 } 230 231 for k, v := range anonymousMappings { 232 if _, has := result[k]; !has { 233 result[k] = v 234 } 235 } 236 return result 237 } 238 239 func getTagValues(field reflect.StructField, mappedKeyTag string) string { 240 key := field.Tag.Get(mappedKeyTag) 241 key = strings.Split(key, ",")[0] 242 if mappedKeyTag == fieldNameKey { 243 key = field.Name 244 } 245 return key 246 } 247 248 //NewFieldSettingByKey reads field's tags and returns them indexed by passed in key, fieldName is always part of the resulting map unless filed has "transient" tag. 249 func NewFieldSettingByKey(aStruct interface{}, key string) map[string](map[string]string) { 250 return BuildTagMapping(aStruct, key, "transient", true, true, columnMapping) 251 } 252 253 func setEmptyMap(source reflect.Value, dataTypes map[string]bool) { 254 if !source.CanSet() { 255 return 256 } 257 mapType := source.Type() 258 259 mapPointer := reflect.New(mapType) 260 261 mapValueType := mapType.Elem() 262 mapKeyType := mapType.Key() 263 264 newMap := mapPointer.Elem() 265 266 newMap.Set(reflect.MakeMap(mapType)) 267 targetMapKeyPointer := reflect.New(mapKeyType) 268 269 targetMapValuePointer := reflect.New(mapValueType) 270 271 var elementKey = targetMapKeyPointer.Elem() 272 var elementValue = targetMapValuePointer.Elem() 273 274 if elementValue.Kind() == reflect.Ptr && elementValue.IsNil() { 275 component := reflect.New(elementValue.Type().Elem()) 276 elementValue.Set(component) 277 } 278 if elementKey.Type() != mapKeyType { 279 if elementKey.Type().AssignableTo(mapKeyType) { 280 elementKey = elementKey.Convert(mapKeyType) 281 } 282 } 283 284 if DereferenceType(elementValue.Type()).Kind() == reflect.Struct { 285 initStruct(elementValue.Interface(), dataTypes) 286 } 287 288 newMap.SetMapIndex(elementKey, elementValue) 289 var elem = mapPointer.Elem() 290 source.Set(elem) 291 } 292 293 func createEmptySlice(source reflect.Value, dataTypes map[string]bool) { 294 sliceType := DiscoverTypeByKind(source.Type(), reflect.Slice) 295 if !source.CanSet() { 296 return 297 } 298 slicePointer := reflect.New(sliceType) 299 slice := slicePointer.Elem() 300 componentType := DiscoverComponentType(sliceType) 301 var targetComponentPointer = reflect.New(componentType) 302 var targetComponent = targetComponentPointer.Elem() 303 if DereferenceType(componentType).Kind() == reflect.Struct { 304 componentType := targetComponent.Type() 305 isPointer := componentType.Kind() == reflect.Ptr 306 if isPointer { 307 componentType = componentType.Elem() 308 } 309 structElement := reflect.New(componentType) 310 initStruct(structElement.Interface(), dataTypes) 311 312 if isPointer { 313 targetComponentPointer.Elem().Set(structElement) 314 } else { 315 targetComponentPointer.Elem().Set(structElement.Elem()) 316 } 317 initStruct(targetComponentPointer.Elem().Interface(), dataTypes) 318 } 319 slice.Set(reflect.Append(slice, targetComponentPointer.Elem())) 320 source.Set(slicePointer.Elem()) 321 } 322 323 //InitStruct initialise any struct pointer to empty struct 324 func InitStruct(source interface{}) { 325 var dataTypes = make(map[string]bool) 326 if source == nil { 327 return 328 } 329 initStruct(source, dataTypes) 330 } 331 332 func initStruct(source interface{}, dataTypes map[string]bool) { 333 if source == nil { 334 return 335 } 336 337 if !IsStruct(source) { 338 return 339 } 340 341 var key = DereferenceType(source).Name() 342 if _, has := dataTypes[key]; has { 343 return 344 } 345 dataTypes[key] = true 346 347 sourceValue, ok := source.(reflect.Value) 348 if !ok { 349 sourceValue = reflect.ValueOf(source) 350 } 351 352 if sourceValue.Type().Kind() == reflect.Ptr { 353 elem := sourceValue.Elem() 354 if elem.Kind() == reflect.Ptr && elem.IsNil() { 355 return 356 } 357 if !sourceValue.Elem().IsValid() { 358 return 359 } 360 } 361 362 _ = ProcessStruct(source, func(fieldType reflect.StructField, fieldValue reflect.Value) error { 363 if !fieldValue.CanInterface() { 364 return nil 365 } 366 367 if fieldValue.Kind() == reflect.String && fieldValue.CanSet() { 368 fieldValue.SetString(" ") 369 return nil 370 } 371 372 if fieldType.Type.Kind() == reflect.Map { 373 setEmptyMap(fieldValue, dataTypes) 374 return nil 375 } 376 if fieldType.Type.Kind() == reflect.Slice { 377 createEmptySlice(fieldValue, dataTypes) 378 return nil 379 } 380 if fieldType.Type.Kind() != reflect.Ptr { 381 return nil 382 } 383 if DereferenceType(fieldType).Kind() == reflect.Struct { 384 if !fieldValue.CanSet() { 385 return nil 386 } 387 if fieldValue.Type().Kind() == reflect.Ptr { 388 fieldStruct := reflect.New(fieldValue.Type().Elem()) 389 390 if reflect.TypeOf(source) != fieldStruct.Type() { 391 initStruct(fieldStruct.Interface(), dataTypes) 392 } 393 fieldValue.Set(fieldStruct) 394 } 395 396 } 397 return nil 398 }) 399 } 400 401 //StructFieldMeta represents struct field meta 402 type StructFieldMeta struct { 403 Name string `json:"name,omitempty"` 404 Type string `json:"type,omitempty"` 405 Required bool `json:"required,"` 406 Description string `json:"description,omitempty"` 407 } 408 409 //StructMeta represents struct meta details 410 type StructMeta struct { 411 Type string 412 rawType reflect.Type `json:"-"` 413 Fields []*StructFieldMeta `json:"fields,omitempty"` 414 Dependencies []*StructMeta `json:"dependencies,omitempty"` 415 } 416 417 func (m *StructMeta) Message() map[string]interface{} { 418 var result = make(map[string]interface{}) 419 var deps = make(map[string]*StructMeta) 420 for _, dep := range m.Dependencies { 421 deps[dep.Type] = dep 422 } 423 for _, field := range m.Fields { 424 if dep, ok := deps[field.Type]; ok { 425 result[field.Name] = dep.Message() 426 continue 427 } 428 result[field.Name] = "" 429 } 430 return result 431 } 432 433 //StructMetaFilter 434 type StructMetaFilter func(field reflect.StructField) bool 435 436 func DefaultStructMetaFilter(ield reflect.StructField) bool { 437 return true 438 } 439 440 var structMetaFilter StructMetaFilter = DefaultStructMetaFilter 441 442 //SetStructMetaFilter sets struct meta filter 443 func SetStructMetaFilter(filter StructMetaFilter) error { 444 if filter == nil { 445 return errors.New("filter was nil") 446 } 447 structMetaFilter = filter 448 return nil 449 } 450 451 //GetStructMeta returns struct meta 452 func GetStructMeta(source interface{}) *StructMeta { 453 var result = &StructMeta{} 454 var trackedTypes = make(map[string]bool) 455 getStructMeta(source, result, trackedTypes) 456 return result 457 } 458 459 //InitStruct initialise any struct pointer to empty struct 460 func getStructMeta(source interface{}, meta *StructMeta, trackedTypes map[string]bool) bool { 461 if source == nil { 462 return false 463 } 464 465 var structType = fmt.Sprintf("%T", source) 466 if _, has := trackedTypes[structType]; has { 467 return false 468 } 469 470 meta.Type = structType 471 meta.Fields = make([]*StructFieldMeta, 0) 472 meta.Dependencies = make([]*StructMeta, 0) 473 sourceValue := reflect.ValueOf(source) 474 475 if sourceValue.Kind() == reflect.Ptr { 476 elem := sourceValue.Elem() 477 if elem.Kind() == reflect.Ptr && elem.IsNil() { 478 return false 479 } 480 481 if !sourceValue.Elem().IsValid() { 482 source = reflect.New(sourceValue.Type().Elem()).Interface() 483 } 484 } 485 486 meta.rawType = sourceValue.Type() 487 trackedTypes[structType] = true 488 _ = ProcessStruct(source, func(fieldType reflect.StructField, field reflect.Value) error { 489 if !structMetaFilter(fieldType) { 490 return nil 491 } 492 if isExported := fieldType.PkgPath == ""; !isExported { 493 structField := &StructField{ 494 Owner: reflect.ValueOf(source), 495 Type: fieldType, 496 Value: field, 497 } 498 if !onUnexportedHandler(structField) { 499 return nil 500 } 501 field = structField.Value 502 } 503 504 if isJSONSkippable(string(fieldType.Tag)) { 505 return nil 506 } 507 fieldMeta := &StructFieldMeta{} 508 fieldMeta.Name = fieldType.Name 509 fieldMeta.Type = fieldType.Type.Name() 510 meta.Fields = append(meta.Fields, fieldMeta) 511 512 if value, ok := fieldType.Tag.Lookup("required"); ok { 513 fieldMeta.Required = AsBoolean(value) 514 } 515 if value, ok := fieldType.Tag.Lookup("description"); ok { 516 fieldMeta.Description = value 517 } 518 var value = field.Interface() 519 if value == nil { 520 return nil 521 } 522 fieldMeta.Type = fmt.Sprintf("%T", value) 523 if fieldType.PkgPath != "" { 524 fieldMeta.Type = strings.Replace(fieldMeta.Type, "*", "", 1) 525 } 526 527 if IsStruct(value) { 528 var fieldStruct = &StructMeta{} 529 switch field.Kind() { 530 case reflect.Ptr: 531 var fieldValue interface{} 532 if field.IsNil() { 533 fieldValue = reflect.New(field.Type().Elem()).Interface() 534 } else { 535 fieldValue = field.Elem().Interface() 536 } 537 if getStructMeta(fieldValue, fieldStruct, trackedTypes) { 538 meta.Dependencies = append(meta.Dependencies, fieldStruct) 539 } 540 541 case reflect.Struct: 542 if field.CanInterface() { 543 if getStructMeta(field.Interface(), fieldStruct, trackedTypes) { 544 meta.Dependencies = append(meta.Dependencies, fieldStruct) 545 } 546 } 547 548 } 549 550 return nil 551 } 552 if IsMap(value) { 553 var aMap = AsMap(field.Interface()) 554 var mapValue interface{} 555 for _, mapValue = range aMap { 556 break 557 } 558 if mapValue != nil && IsStruct(mapValue) { 559 var fieldStruct = &StructMeta{} 560 if getStructMeta(mapValue, fieldStruct, trackedTypes) { 561 meta.Dependencies = append(meta.Dependencies, fieldStruct) 562 563 } 564 } 565 return nil 566 } 567 if IsSlice(value) { 568 var aSlice = AsSlice(field.Interface()) 569 if len(aSlice) > 0 { 570 if aSlice[0] != nil && IsStruct(aSlice[0]) { 571 var fieldStruct = &StructMeta{} 572 if getStructMeta(aSlice[0], fieldStruct, trackedTypes) { 573 meta.Dependencies = append(meta.Dependencies, fieldStruct) 574 } 575 } 576 } 577 return nil 578 } 579 return nil 580 }) 581 return true 582 } 583 584 func isJSONSkippable(tag string) bool { 585 return strings.Contains(tag, "json:\"-") 586 } 587 588 //StructFields by name sorter 589 type StructFields []*StructField 590 591 // Len is part of sort.Interface. 592 func (s StructFields) Len() int { 593 return len(s) 594 } 595 596 // Swap is part of sort.Interface. 597 func (s StructFields) Swap(i, j int) { 598 s[i], s[j] = s[j], s[i] 599 } 600 601 // Less is part of sort.Interface. 602 func (s StructFields) Less(i, j int) bool { 603 return s[i].Type.Name < s[j].Type.Name 604 }