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  }