github.com/gogf/gf/v2@v2.7.4/util/gconv/internal/structcache/structcache_cached_field_info.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 structcache
     8  
     9  import (
    10  	"reflect"
    11  	"sync/atomic"
    12  )
    13  
    14  // CachedFieldInfo holds the cached info for struct field.
    15  type CachedFieldInfo struct {
    16  	// WARN:
    17  	//  The [CachedFieldInfoBase] structure cannot be merged with the following [IsField] field into one structure.
    18  	// 	The [IsField] field should be used separately in the [bindStructWithLoopParamsMap] method
    19  	*CachedFieldInfoBase
    20  
    21  	// This field is mainly used in the [bindStructWithLoopParamsMap] method.
    22  	// This field is needed when both `fieldName` and `tag` of a field exist in the map.
    23  	// For example:
    24  	// field string `json:"name"`
    25  	// map = {
    26  	//     "field" : "f1",
    27  	//     "name" : "n1",
    28  	// }
    29  	// The `name` should be used here.
    30  	// In the bindStructWithLoopParamsMap method, due to the disorder of `map`, `field` may be traversed first.
    31  	// This field is more about priority, that is, the priority of `name` is higher than that of `field`,
    32  	// even if it has been set before.
    33  	IsField bool
    34  }
    35  
    36  // CachedFieldInfoBase holds the cached info for struct field.
    37  type CachedFieldInfoBase struct {
    38  	// FieldIndexes holds the global index number from struct info.
    39  	// The field may belong to an embedded structure, so it is defined here as []int.
    40  	FieldIndexes []int
    41  
    42  	// PriorityTagAndFieldName holds the tag value(conv, param, p, c, json) and the field name.
    43  	// PriorityTagAndFieldName contains the field name, which is the last item of slice.
    44  	PriorityTagAndFieldName []string
    45  
    46  	// IsCommonInterface marks this field implements common interfaces as:
    47  	// - iUnmarshalValue
    48  	// - iUnmarshalText
    49  	// - iUnmarshalJSON
    50  	// Purpose: reduce the interface asserting cost in runtime.
    51  	IsCommonInterface bool
    52  
    53  	// IsCustomConvert marks there custom converting function for this field type.
    54  	IsCustomConvert bool
    55  
    56  	// StructField is the type info of this field.
    57  	StructField reflect.StructField
    58  
    59  	// OtherSameNameField stores fields with the same name and type or different types of nested structures.
    60  	//
    61  	// For example:
    62  	// type ID struct{
    63  	//     ID1  string
    64  	//     ID2 int
    65  	// }
    66  	// type Card struct{
    67  	//     ID
    68  	//     ID1  uint64
    69  	//     ID2 int64
    70  	// }
    71  	//
    72  	// We will cache each ID1 and ID2 separately,
    73  	// even if their types are different and their indexes are different
    74  	OtherSameNameField []*CachedFieldInfo
    75  
    76  	// ConvertFunc is the converting function for this field.
    77  	ConvertFunc func(from any, to reflect.Value)
    78  
    79  	// The last fuzzy matching key for this field.
    80  	// The fuzzy matching occurs only if there are no direct tag and field name matching in the params map.
    81  	// TODO If different paramsMaps contain paramKeys in different formats and all hit the same fieldName,
    82  	//      the cached value may be continuously updated.
    83  	// LastFuzzyKey string.
    84  	LastFuzzyKey atomic.Value
    85  
    86  	// removeSymbolsFieldName is used for quick fuzzy match for parameter key.
    87  	// removeSymbolsFieldName = utils.RemoveSymbols(fieldName)
    88  	RemoveSymbolsFieldName string
    89  }
    90  
    91  // FieldName returns the field name of current field info.
    92  func (cfi *CachedFieldInfo) FieldName() string {
    93  	return cfi.PriorityTagAndFieldName[len(cfi.PriorityTagAndFieldName)-1]
    94  }
    95  
    96  // GetFieldReflectValueFrom retrieves and returns the `reflect.Value` of given struct field,
    97  // which is used for directly value assignment.
    98  //
    99  // Note that, the input parameter `structValue` might be initialized internally.
   100  func (cfi *CachedFieldInfo) GetFieldReflectValueFrom(structValue reflect.Value) reflect.Value {
   101  	if len(cfi.FieldIndexes) == 1 {
   102  		// no nested struct.
   103  		return structValue.Field(cfi.FieldIndexes[0])
   104  	}
   105  	return cfi.fieldReflectValue(structValue, cfi.FieldIndexes)
   106  }
   107  
   108  // GetOtherFieldReflectValueFrom retrieves and returns the `reflect.Value` of given struct field with nested index
   109  // by `fieldLevel`, which is used for directly value assignment.
   110  //
   111  // Note that, the input parameter `structValue` might be initialized internally.
   112  func (cfi *CachedFieldInfo) GetOtherFieldReflectValueFrom(structValue reflect.Value, fieldIndex []int) reflect.Value {
   113  	if len(fieldIndex) == 1 {
   114  		// no nested struct.
   115  		return structValue.Field(fieldIndex[0])
   116  	}
   117  	return cfi.fieldReflectValue(structValue, fieldIndex)
   118  }
   119  
   120  func (cfi *CachedFieldInfo) fieldReflectValue(v reflect.Value, fieldIndexes []int) reflect.Value {
   121  	for i, x := range fieldIndexes {
   122  		if i > 0 {
   123  			// it means nested struct.
   124  			switch v.Kind() {
   125  			case reflect.Pointer:
   126  				if v.IsNil() {
   127  					// Initialization.
   128  					v.Set(reflect.New(v.Type().Elem()))
   129  				}
   130  				v = v.Elem()
   131  
   132  			case reflect.Interface:
   133  				// Compatible with previous code
   134  				// Interface => struct
   135  				v = v.Elem()
   136  				if v.Kind() == reflect.Ptr {
   137  					// maybe *struct or other types
   138  					v = v.Elem()
   139  				}
   140  			default:
   141  			}
   142  		}
   143  		v = v.Field(x)
   144  	}
   145  	return v
   146  }