From 7f030248ac1028f29a6ff6c9e984cd93cd4339c5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 Mar 2025 11:15:22 +0800 Subject: [PATCH] up --- util/gconv/gconv.go | 20 +-- util/gconv/gconv_convert.go | 47 ++++--- util/gconv/gconv_convert_config.go | 99 +++++++++++++++ util/gconv/gconv_converter.go | 46 ++++--- util/gconv/gconv_converter_builtin.go | 82 ++++++++++++ util/gconv/gconv_maptomap.go | 34 ++--- util/gconv/gconv_scan.go | 13 +- util/gconv/gconv_struct.go | 117 ++++++++++------- .../gconv/internal/structcache/structcache.go | 50 +++++++- .../structcache/structcache_cached.go | 53 ++------ .../structcache_cached_field_info.go | 2 +- .../structcache_cached_struct_info.go | 119 ++++++++---------- 12 files changed, 442 insertions(+), 240 deletions(-) create mode 100644 util/gconv/gconv_convert_config.go create mode 100644 util/gconv/gconv_converter_builtin.go diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index d3bc027ef..426afd401 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -14,6 +14,8 @@ import ( "github.com/gogf/gf/v2/util/gconv/internal/structcache" ) +type AnyConvertFunc = structcache.AnyConvertFunc + var ( // Empty strings. emptyStringMap = map[string]struct{}{ @@ -29,17 +31,7 @@ var ( // Note that only pointer can implement interface IUnmarshalValue. type IUnmarshalValue = localinterface.IUnmarshalValue -func init() { - // register common converters for internal usage. - structcache.RegisterCommonConverter(structcache.CommonConverter{ - Int64: Int64, - Uint64: Uint64, - String: String, - Float32: Float32, - Float64: Float64, - Time: Time, - GTime: GTime, - Bytes: Bytes, - Bool: Bool, - }) -} +var ( + // defaultConvertConfig is the default configuration for type converting. + defaultConvertConfig = NewConvertConfig() +) diff --git a/util/gconv/gconv_convert.go b/util/gconv/gconv_convert.go index 49bb32b1f..2cd67d3e8 100644 --- a/util/gconv/gconv_convert.go +++ b/util/gconv/gconv_convert.go @@ -21,12 +21,15 @@ import ( // The optional parameter `extraParams` is used for additional necessary parameter for this conversion. // It supports common basic types conversion as its conversion based on type name string. func Convert(fromValue interface{}, toTypeName string, extraParams ...interface{}) interface{} { - return doConvert(doConvertInput{ - FromValue: fromValue, - ToTypeName: toTypeName, - ReferValue: nil, - Extra: extraParams, - }) + return doConvert( + defaultConvertConfig, + doConvertInput{ + FromValue: fromValue, + ToTypeName: toTypeName, + ReferValue: nil, + Extra: extraParams, + }, + ) } // ConvertWithRefer converts the variable `fromValue` to the type referred by value `referValue`. @@ -40,12 +43,15 @@ func ConvertWithRefer(fromValue interface{}, referValue interface{}, extraParams } else { referValueRf = reflect.ValueOf(referValue) } - return doConvert(doConvertInput{ - FromValue: fromValue, - ToTypeName: referValueRf.Type().String(), - ReferValue: referValue, - Extra: extraParams, - }) + return doConvert( + defaultConvertConfig, + doConvertInput{ + FromValue: fromValue, + ToTypeName: referValueRf.Type().String(), + ReferValue: referValue, + Extra: extraParams, + }, + ) } type doConvertInput struct { @@ -53,13 +59,14 @@ type doConvertInput struct { ToTypeName string // Target value type name in string. ReferValue interface{} // Referred value, a value in type `ToTypeName`. Note that its type might be reflect.Value. Extra []interface{} // Extra values for implementing the converting. + // Marks that the value is already converted and set to `ReferValue`. Caller can ignore the returned result. // It is an attribute for internal usage purpose. alreadySetToReferValue bool } // doConvert does commonly use types converting. -func doConvert(in doConvertInput) (convertedValue interface{}) { +func doConvert(cf *ConvertConfig, in doConvertInput) (convertedValue interface{}) { switch in.ToTypeName { case "int": return Int(in.FromValue) @@ -296,14 +303,14 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { } // custom converter. - if dstReflectValue, ok, _ := callCustomConverterWithRefer(fromReflectValue, referReflectValue); ok { + if dstReflectValue, ok, _ := cf.callCustomConverterWithRefer(fromReflectValue, referReflectValue); ok { return dstReflectValue.Interface() } defer func() { if recover() != nil { in.alreadySetToReferValue = false - if err := bindVarToReflectValue(referReflectValue, in.FromValue, nil); err == nil { + if err := bindVarToReflectValue(cf, referReflectValue, in.FromValue, nil); err == nil { in.alreadySetToReferValue = true convertedValue = referReflectValue.Interface() } @@ -326,7 +333,7 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { default: in.ToTypeName = originType.Kind().String() in.ReferValue = nil - refElementValue := reflect.ValueOf(doConvert(in)) + refElementValue := reflect.ValueOf(doConvert(cf, in)) originTypeValue := reflect.New(refElementValue.Type()).Elem() originTypeValue.Set(refElementValue) in.alreadySetToReferValue = true @@ -335,7 +342,7 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { case reflect.Map: var targetValue = reflect.New(referReflectValue.Type()).Elem() - if err := doMapToMap(in.FromValue, targetValue); err == nil { + if err := doMapToMap(cf, in.FromValue, targetValue); err == nil { in.alreadySetToReferValue = true } return targetValue.Interface() @@ -343,15 +350,15 @@ func doConvert(in doConvertInput) (convertedValue interface{}) { in.ToTypeName = referReflectValue.Kind().String() in.ReferValue = nil in.alreadySetToReferValue = true - convertedValue = reflect.ValueOf(doConvert(in)).Convert(referReflectValue.Type()).Interface() + convertedValue = reflect.ValueOf(doConvert(cf, in)).Convert(referReflectValue.Type()).Interface() return convertedValue } return in.FromValue } } -func doConvertWithReflectValueSet(reflectValue reflect.Value, in doConvertInput) { - convertedValue := doConvert(in) +func doConvertWithReflectValueSet(cf *ConvertConfig, reflectValue reflect.Value, in doConvertInput) { + convertedValue := doConvert(cf, in) if !in.alreadySetToReferValue { reflectValue.Set(reflect.ValueOf(convertedValue)) } diff --git a/util/gconv/gconv_convert_config.go b/util/gconv/gconv_convert_config.go new file mode 100644 index 000000000..82712c077 --- /dev/null +++ b/util/gconv/gconv_convert_config.go @@ -0,0 +1,99 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv + +import ( + "reflect" + "time" + + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv/internal/structcache" +) + +// CommonTypeConverter holds some converting functions of common types for internal usage. +type CommonTypeConverter = structcache.CommonTypeConverter + +type ( + converterInType = reflect.Type + converterOutType = reflect.Type + converterFunc = reflect.Value +) + +// ConvertConfig is the configuration for type converting. +type ConvertConfig struct { + internalConvertConfig *structcache.ConvertConfig + // customConverters for internal converter storing. + customConverters map[converterInType]map[converterOutType]converterFunc +} + +var ( + intType = reflect.TypeOf(0) + int8Type = reflect.TypeOf(int8(0)) + int16Type = reflect.TypeOf(int16(0)) + int32Type = reflect.TypeOf(int32(0)) + int64Type = reflect.TypeOf(int64(0)) + + uintType = reflect.TypeOf(uint(0)) + uint8Type = reflect.TypeOf(uint8(0)) + uint16Type = reflect.TypeOf(uint16(0)) + uint32Type = reflect.TypeOf(uint32(0)) + uint64Type = reflect.TypeOf(uint64(0)) + + float32Type = reflect.TypeOf(float32(0)) + float64Type = reflect.TypeOf(float64(0)) + + stringType = reflect.TypeOf("") + bytesType = reflect.TypeOf([]byte{}) + + boolType = reflect.TypeOf(false) + + timeType = reflect.TypeOf((*time.Time)(nil)).Elem() + gtimeType = reflect.TypeOf((*gtime.Time)(nil)).Elem() +) + +// NewConvertConfig creates and returns configuration management object for type converting. +func NewConvertConfig() *ConvertConfig { + cf := &ConvertConfig{ + internalConvertConfig: structcache.NewConvertConfig(), + customConverters: make(map[converterInType]map[converterOutType]converterFunc), + } + cf.registerBuiltInConverter() + return cf +} + +func (cf *ConvertConfig) registerBuiltInConverter() { + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForInt64, intType, int8Type, int16Type, int32Type, int64Type, + ) + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForUint64, uintType, uint8Type, uint16Type, uint32Type, uint64Type, + ) + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForString, stringType, + ) + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForFloat64, float32Type, float64Type, + ) + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForBool, boolType, + ) + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForBytes, bytesType, + ) + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForTime, timeType, + ) + cf.registerAnyConvertFuncForTypes( + builtInAnyConvertFuncForGTime, gtimeType, + ) +} + +func (cf *ConvertConfig) registerAnyConvertFuncForTypes(convertFunc AnyConvertFunc, types ...reflect.Type) { + for _, t := range types { + cf.internalConvertConfig.RegisterAnyConvertFunc(t, convertFunc) + } +} diff --git a/util/gconv/gconv_converter.go b/util/gconv/gconv_converter.go index eb0e226d1..72337f00f 100644 --- a/util/gconv/gconv_converter.go +++ b/util/gconv/gconv_converter.go @@ -11,19 +11,14 @@ import ( "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/util/gconv/internal/structcache" ) -type ( - converterInType = reflect.Type - converterOutType = reflect.Type - converterFunc = reflect.Value -) +// RegisterConverter registers custom converter. +func RegisterConverter(fn any) (err error) { + return defaultConvertConfig.RegisterConverter(fn) +} -// customConverters for internal converter storing. -var customConverters = make(map[converterInType]map[converterOutType]converterFunc) - -// RegisterConverter to register custom converter. +// RegisterConverter registers custom converter. // It must be registered before you use this custom converting feature. // It is suggested to do it in boot procedure of the process. // @@ -31,7 +26,7 @@ var customConverters = make(map[converterInType]map[converterOutType]converterFu // 1. The parameter `fn` must be defined as pattern `func(T1) (T2, error)`. // It will convert type `T1` to type `T2`. // 2. The `T1` should not be type of pointer, but the `T2` should be type of pointer. -func RegisterConverter(fn interface{}) (err error) { +func (cf *ConvertConfig) RegisterConverter(fn any) (err error) { var ( fnReflectType = reflect.TypeOf(fn) errType = reflect.TypeOf((*error)(nil)).Elem() @@ -41,7 +36,8 @@ func RegisterConverter(fn interface{}) (err error) { !fnReflectType.Out(1).Implements(errType) { err = gerror.NewCodef( gcode.CodeInvalidParameter, - "parameter must be type of converter function and defined as pattern `func(T1) (T2, error)`, but defined as `%s`", + "parameter must be type of converter function and defined as pattern `func(T1) (T2, error)`, "+ + "but defined as `%s`", fnReflectType.String(), ) return @@ -69,10 +65,10 @@ func RegisterConverter(fn interface{}) (err error) { return } - registeredOutTypeMap, ok := customConverters[inType] + registeredOutTypeMap, ok := cf.customConverters[inType] if !ok { registeredOutTypeMap = make(map[converterOutType]converterFunc) - customConverters[inType] = registeredOutTypeMap + cf.customConverters[inType] = registeredOutTypeMap } if _, ok = registeredOutTypeMap[outType]; ok { err = gerror.NewCodef( @@ -83,14 +79,14 @@ func RegisterConverter(fn interface{}) (err error) { return } registeredOutTypeMap[outType] = reflect.ValueOf(fn) - structcache.RegisterCustomConvertType(outType) + cf.internalConvertConfig.RegisterCustomConvertType(outType) return } -func getRegisteredConverterFuncAndSrcType( +func (cf *ConvertConfig) getRegisteredConverterFuncAndSrcType( srcReflectValue, dstReflectValueForRefer reflect.Value, ) (f converterFunc, srcType reflect.Type, ok bool) { - if len(customConverters) == 0 { + if len(cf.customConverters) == 0 { return reflect.Value{}, nil, false } srcType = srcReflectValue.Type() @@ -99,7 +95,7 @@ func getRegisteredConverterFuncAndSrcType( } var registeredOutTypeMap map[converterOutType]converterFunc // firstly, it searches the map by input parameter type. - registeredOutTypeMap, ok = customConverters[srcType] + registeredOutTypeMap, ok = cf.customConverters[srcType] if !ok { return reflect.Value{}, nil, false } @@ -123,28 +119,28 @@ func getRegisteredConverterFuncAndSrcType( return } -func callCustomConverterWithRefer( +func (cf *ConvertConfig) callCustomConverterWithRefer( srcReflectValue, referReflectValue reflect.Value, ) (dstReflectValue reflect.Value, converted bool, err error) { - registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue) + registeredConverterFunc, srcType, ok := cf.getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue) if !ok { return reflect.Value{}, false, nil } dstReflectValue = reflect.New(referReflectValue.Type()).Elem() - converted, err = doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) + converted, err = cf.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) return } // callCustomConverter call the custom converter. It will try some possible type. -func callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) { - registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue) +func (cf *ConvertConfig) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) { + registeredConverterFunc, srcType, ok := cf.getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue) if !ok { return false, nil } - return doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) + return cf.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType) } -func doCallCustomConverter( +func (cf *ConvertConfig) doCallCustomConverter( srcReflectValue reflect.Value, dstReflectValue reflect.Value, registeredConverterFunc converterFunc, diff --git a/util/gconv/gconv_converter_builtin.go b/util/gconv/gconv_converter_builtin.go new file mode 100644 index 000000000..1d2e00594 --- /dev/null +++ b/util/gconv/gconv_converter_builtin.go @@ -0,0 +1,82 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gconv + +import ( + "reflect" + "time" + + "github.com/gogf/gf/v2/os/gtime" +) + +func builtInAnyConvertFuncForInt64(from any, to reflect.Value) error { + v, err := doInt64(from) + if err != nil { + return err + } + to.SetInt(v) + return nil +} + +func builtInAnyConvertFuncForUint64(from any, to reflect.Value) error { + v, err := doUint64(from) + if err != nil { + return err + } + to.SetUint(v) + return nil +} + +func builtInAnyConvertFuncForString(from any, to reflect.Value) error { + v, err := doString(from) + if err != nil { + return err + } + to.SetString(v) + return nil +} + +func builtInAnyConvertFuncForFloat64(from any, to reflect.Value) error { + v, err := doFloat64(from) + if err != nil { + return err + } + to.SetFloat(v) + return nil +} + +func builtInAnyConvertFuncForBool(from any, to reflect.Value) error { + v, err := doBool(from) + if err != nil { + return err + } + to.SetBool(v) + return nil +} + +func builtInAnyConvertFuncForBytes(from any, to reflect.Value) error { + v, err := doBytes(from) + if err != nil { + return err + } + to.SetBytes(v) + return nil +} + +func builtInAnyConvertFuncForTime(from any, to reflect.Value) error { + *to.Addr().Interface().(*time.Time) = Time(from) + return nil +} + +func builtInAnyConvertFuncForGTime(from any, to reflect.Value) error { + v := GTime(from) + if v == nil { + v = gtime.New() + } + *to.Addr().Interface().(*gtime.Time) = *v + return nil +} diff --git a/util/gconv/gconv_maptomap.go b/util/gconv/gconv_maptomap.go index ffd352e58..bc40a82c0 100644 --- a/util/gconv/gconv_maptomap.go +++ b/util/gconv/gconv_maptomap.go @@ -16,7 +16,7 @@ import ( // MapToMap converts any map type variable `params` to another map type variable `pointer` // using reflect. // See doMapToMap. -func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error { +func MapToMap(params any, pointer any, mapping ...map[string]string) error { return Scan(params, pointer, mapping...) } @@ -30,7 +30,7 @@ func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]str // // The optional parameter `mapping` is used for struct attribute to map key mapping, which makes // sense only if the items of original map `params` is type struct. -func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) { +func doMapToMap(cf *ConvertConfig, params any, pointer any, mapping ...map[string]string) (err error) { var ( paramsRv reflect.Value paramsKind reflect.Kind @@ -50,7 +50,7 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s paramsKind = paramsRv.Kind() } if paramsKind != reflect.Map { - return doMapToMap(Map(params), pointer, mapping...) + return doMapToMap(cf, Map(params), pointer, mapping...) } // Empty params map, no need continue. if paramsRv.Len() == 0 { @@ -107,22 +107,26 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s default: mapValue.Set( reflect.ValueOf( - doConvert(doConvertInput{ - FromValue: paramsRv.MapIndex(key).Interface(), - ToTypeName: pointerValueType.String(), - ReferValue: mapValue, - Extra: nil, - }), + doConvert( + cf, doConvertInput{ + FromValue: paramsRv.MapIndex(key).Interface(), + ToTypeName: pointerValueType.String(), + ReferValue: mapValue, + Extra: nil, + }), ), ) } var mapKey = reflect.ValueOf( - doConvert(doConvertInput{ - FromValue: key.Interface(), - ToTypeName: pointerKeyType.Name(), - ReferValue: reflect.New(pointerKeyType).Elem().Interface(), - Extra: nil, - }), + doConvert( + cf, + doConvertInput{ + FromValue: key.Interface(), + ToTypeName: pointerKeyType.Name(), + ReferValue: reflect.New(pointerKeyType).Elem().Interface(), + Extra: nil, + }, + ), ) dataMap.SetMapIndex(mapKey, mapValue) } diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 376a2b1aa..5ced0c5d8 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -26,6 +26,10 @@ import ( // The `paramKeyToAttrMap` parameter is used for mapping between attribute names and parameter keys. // TODO: change `paramKeyToAttrMap` to `ScanOption` to be more scalable; add `DeepCopy` option for `ScanOption`. func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) (err error) { + return ScanWithConfig(defaultConvertConfig, srcValue, dstPointer, paramKeyToAttrMap...) +} + +func ScanWithConfig(cf *ConvertConfig, srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) (err error) { // Check if srcValue is nil, in which case no conversion is needed if srcValue == nil { return nil @@ -142,7 +146,7 @@ func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) } // Special handling for struct or map slice elements if dstElemKind == reflect.Struct || dstElemKind == reflect.Map { - return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) + return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) } // Handle basic type slice conversions var srcValueReflectValueKind = srcValueReflectValue.Kind() @@ -173,11 +177,11 @@ func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) dstPointerReflectValueElem.Set(newSlice) return nil } - return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) + return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) default: // Handle complex types (structs, maps, etc.) - return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) + return doScanForComplicatedTypes(cf, srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...) } } @@ -193,6 +197,7 @@ func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) // - dstPointerReflectType: The reflection type of the destination pointer // - paramKeyToAttrMap: Optional mapping between parameter keys and struct attribute names func doScanForComplicatedTypes( + cf *ConvertConfig, srcValue, dstPointer any, dstPointerReflectType reflect.Type, paramKeyToAttrMap ...map[string]string, @@ -220,7 +225,7 @@ func doScanForComplicatedTypes( switch dstPointerReflectTypeElemKind { case reflect.Map: // Convert map to map - return doMapToMap(srcValue, dstPointer, paramKeyToAttrMap...) + return doMapToMap(cf, srcValue, dstPointer, paramKeyToAttrMap...) case reflect.Array, reflect.Slice: var ( diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 8c7e1aa76..e47ee395f 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -32,20 +32,34 @@ import ( // It will automatically convert the first letter of the key to uppercase // in mapping procedure to do the matching. // It ignores the map key, if it does not match. -func Struct(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) { +func Struct(params any, pointer any, paramKeyToAttrMap ...map[string]string) (err error) { return Scan(params, pointer, paramKeyToAttrMap...) } // StructTag acts as Struct but also with support for priority tag feature, which retrieves the // specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping. // The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','. -func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) { +func StructTag(params any, pointer any, priorityTag string) (err error) { return doStruct(params, pointer, nil, priorityTag) } // doStruct is the core internal converting function for any data to struct. func doStruct( - params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string, + params any, + pointer any, + paramKeyToAttrMap map[string]string, + priorityTag string, +) (err error) { + return doStructWithConfig(defaultConvertConfig, params, pointer, paramKeyToAttrMap, priorityTag) +} + +// doStruct is the core internal converting function for any data to struct. +func doStructWithConfig( + cf *ConvertConfig, + params any, + pointer any, + paramKeyToAttrMap map[string]string, + priorityTag string, ) (err error) { if params == nil { // If `params` is nil, no conversion. @@ -77,7 +91,7 @@ func doStruct( var ( paramsReflectValue reflect.Value - paramsInterface interface{} // DO NOT use `params` directly as it might be type `reflect.Value` + paramsInterface any // DO NOT use `params` directly as it might be type `reflect.Value` pointerReflectValue reflect.Value pointerReflectKind reflect.Kind pointerElemReflectValue reflect.Value // The reflection value to struct element. @@ -118,7 +132,7 @@ func doStruct( } // custom convert. - if ok, err = callCustomConverter(paramsReflectValue, pointerReflectValue); ok { + if ok, err = cf.callCustomConverter(paramsReflectValue, pointerReflectValue); ok { return err } @@ -150,15 +164,15 @@ func doStruct( // Retrieve its element, may be struct at last. pointerElemReflectValue = pointerElemReflectValue.Elem() } - paramsMap, ok := paramsInterface.(map[string]interface{}) + paramsMap, ok := paramsInterface.(map[string]any) if !ok { - // paramsMap is the map[string]interface{} type variable for params. + // paramsMap is the map[string]any type variable for params. // DO NOT use MapDeep here. paramsMap = doMapConvert(paramsInterface, recursiveTypeAuto, true) if paramsMap == nil { return gerror.NewCodef( gcode.CodeInvalidParameter, - `convert params from "%#v" to "map[string]interface{}" failed`, + `convert params from "%#v" to "map[string]any" failed`, params, ) } @@ -168,7 +182,7 @@ func doStruct( return nil } // Get struct info from cache or parse struct and cache the struct info. - cachedStructInfo := structcache.GetCachedStructInfo( + cachedStructInfo := cf.internalConvertConfig.GetCachedStructInfo( pointerElemReflectValue.Type(), priorityTag, ) // Nothing to be converted. @@ -184,7 +198,7 @@ func doStruct( // Indicates that those values have been used and cannot be reused. usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool() cachedFieldInfo *structcache.CachedFieldInfo - paramsValue interface{} + paramsValue any ) defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap) @@ -199,16 +213,17 @@ func doStruct( if cachedFieldInfo != nil { fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue) if err = bindVarToStructField( + cf, + cachedFieldInfo, fieldValue, paramsValue, - cachedFieldInfo, paramKeyToAttrMap, ); err != nil { return err } if len(cachedFieldInfo.OtherSameNameField) > 0 { if err = setOtherSameNameField( - cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap, + cf, cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap, ); err != nil { return err } @@ -221,11 +236,12 @@ func doStruct( return nil } return bindStructWithLoopFieldInfos( - paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo, + cf, paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo, ) } func setOtherSameNameField( + cf *ConvertConfig, cachedFieldInfo *structcache.CachedFieldInfo, srcValue any, structValue reflect.Value, @@ -234,7 +250,7 @@ func setOtherSameNameField( // loop the same field name of all sub attributes. for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField { fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes) - if err = bindVarToStructField(fieldValue, srcValue, otherFieldInfo, paramKeyToAttrMap); err != nil { + if err = bindVarToStructField(cf, otherFieldInfo, fieldValue, srcValue, paramKeyToAttrMap); err != nil { return err } } @@ -242,6 +258,7 @@ func setOtherSameNameField( } func bindStructWithLoopFieldInfos( + cf *ConvertConfig, paramsMap map[string]any, structValue reflect.Value, paramKeyToAttrMap map[string]string, @@ -257,21 +274,21 @@ func bindStructWithLoopFieldInfos( matched bool ok bool ) - for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos { + for _, cachedFieldInfo = range cachedStructInfo.GetFieldConvertInfos() { for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName { if paramValue, ok = paramsMap[fieldTag]; !ok { continue } fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue) if err = bindVarToStructField( - fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap, + cf, cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap, ); err != nil { return err } // handle same field name in nested struct. if len(cachedFieldInfo.OtherSameNameField) > 0 { if err = setOtherSameNameField( - cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap, + cf, cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap, ); err != nil { return err } @@ -297,14 +314,14 @@ func bindStructWithLoopFieldInfos( fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue) if paramValue != nil { if err = bindVarToStructField( - fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap, + cf, cachedFieldInfo, fieldValue, paramValue, paramKeyToAttrMap, ); err != nil { return err } // handle same field name in nested struct. if len(cachedFieldInfo.OtherSameNameField) > 0 { if err = setOtherSameNameField( - cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap, + cf, cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap, ); err != nil { return err } @@ -338,9 +355,10 @@ func fuzzyMatchingFieldName( // bindVarToStructField sets value to struct object attribute by name. // each value to attribute converting comes into in this function. func bindVarToStructField( - fieldValue reflect.Value, - srcValue interface{}, + cf *ConvertConfig, cachedFieldInfo *structcache.CachedFieldInfo, + fieldValue reflect.Value, + srcValue any, paramKeyToAttrMap map[string]string, ) (err error) { if !fieldValue.IsValid() { @@ -352,7 +370,7 @@ func bindVarToStructField( } defer func() { if exception := recover(); exception != nil { - if err = bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil { + if err = bindVarToReflectValue(cf, fieldValue, srcValue, paramKeyToAttrMap); err != nil { err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName()) } } @@ -372,7 +390,7 @@ func bindVarToStructField( if customConverterInput, ok = srcValue.(reflect.Value); !ok { customConverterInput = reflect.ValueOf(srcValue) } - if ok, err = callCustomConverter(customConverterInput, fieldValue); ok || err != nil { + if ok, err = cf.callCustomConverter(customConverterInput, fieldValue); ok || err != nil { return } } @@ -383,20 +401,21 @@ func bindVarToStructField( } // Common types use fast assignment logic if cachedFieldInfo.ConvertFunc != nil { - cachedFieldInfo.ConvertFunc(srcValue, fieldValue) - return nil + return cachedFieldInfo.ConvertFunc(srcValue, fieldValue) } - doConvertWithReflectValueSet(fieldValue, doConvertInput{ - FromValue: srcValue, - ToTypeName: cachedFieldInfo.StructField.Type.String(), - ReferValue: fieldValue, - }) + doConvertWithReflectValueSet( + cf, fieldValue, doConvertInput{ + FromValue: srcValue, + ToTypeName: cachedFieldInfo.StructField.Type.String(), + ReferValue: fieldValue, + }, + ) return nil } // bindVarToReflectValueWithInterfaceCheck does bind using common interfaces checks. -func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (bool, error) { - var pointer interface{} +func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value any) (bool, error) { + var pointer any if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() { reflectValueAddr := reflectValue.Addr() if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() { @@ -460,7 +479,7 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i // bindVarToReflectValue sets `value` to reflect value object `structFieldValue`. func bindVarToReflectValue( - structFieldValue reflect.Value, value interface{}, paramKeyToAttrMap map[string]string, + cf *ConvertConfig, structFieldValue reflect.Value, value any, paramKeyToAttrMap map[string]string, ) (err error) { // JSON content converting. ok, err := doConvertWithJsonCheck(value, structFieldValue) @@ -486,7 +505,7 @@ func bindVarToReflectValue( // Converting using reflection by kind. switch kind { case reflect.Map: - return doMapToMap(value, structFieldValue, paramKeyToAttrMap) + return doMapToMap(cf, value, structFieldValue, paramKeyToAttrMap) case reflect.Struct: // Recursively converting for struct attribute. @@ -528,11 +547,13 @@ func bindVarToReflectValue( } } if !converted { - doConvertWithReflectValueSet(elem, doConvertInput{ - FromValue: reflectValue.Index(i).Interface(), - ToTypeName: elemTypeName, - ReferValue: elem, - }) + doConvertWithReflectValueSet( + cf, elem, doConvertInput{ + FromValue: reflectValue.Index(i).Interface(), + ToTypeName: elemTypeName, + ReferValue: elem, + }, + ) } if elemType.Kind() == reflect.Ptr { // Before it sets the `elem` to array, do pointer converting if necessary. @@ -578,11 +599,13 @@ func bindVarToReflectValue( } } if !converted { - doConvertWithReflectValueSet(elem, doConvertInput{ - FromValue: value, - ToTypeName: elemTypeName, - ReferValue: elem, - }) + doConvertWithReflectValueSet( + cf, elem, doConvertInput{ + FromValue: value, + ToTypeName: elemTypeName, + ReferValue: elem, + }, + ) } if elemType.Kind() == reflect.Ptr { // Before it sets the `elem` to array, do pointer converting if necessary. @@ -602,19 +625,19 @@ func bindVarToReflectValue( return err } elem := item.Elem() - if err = bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil { + if err = bindVarToReflectValue(cf, elem, value, paramKeyToAttrMap); err == nil { structFieldValue.Set(elem.Addr()) } } else { // Not empty pointer, it assigns values to it. - return bindVarToReflectValue(structFieldValue.Elem(), value, paramKeyToAttrMap) + return bindVarToReflectValue(cf, structFieldValue.Elem(), value, paramKeyToAttrMap) } // It mainly and specially handles the interface of nil value. case reflect.Interface: if value == nil { // Specially. - structFieldValue.Set(reflect.ValueOf((*interface{})(nil))) + structFieldValue.Set(reflect.ValueOf((*any)(nil))) } else { // Note there's reflect conversion mechanism here. structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type())) diff --git a/util/gconv/internal/structcache/structcache.go b/util/gconv/internal/structcache/structcache.go index cdb97388d..e99fd84c6 100644 --- a/util/gconv/internal/structcache/structcache.go +++ b/util/gconv/internal/structcache/structcache.go @@ -9,24 +9,64 @@ package structcache import ( "reflect" + "sync" + "time" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv/internal/localinterface" ) -var ( +type AnyConvertFunc func(from any, to reflect.Value) error + +// ConvertConfig is the configuration for type converting. +type ConvertConfig struct { + // map[reflect.Type]*CachedStructInfo + cachedStructsInfoMap sync.Map + // customConvertTypeMap is used to store whether field types are registered to custom conversions // For example: // func (src *TypeA) (dst *TypeB,err error) // This map will store `TypeB` for quick judgment during assignment. - customConvertTypeMap = map[reflect.Type]struct{}{} -) + // TODO remove? + customConvertTypeMap map[reflect.Type]struct{} + + // anyToTypeConvertMap for custom type converting from any to its reflect.Value. + anyToTypeConvertMap map[reflect.Type]AnyConvertFunc +} + +// CommonTypeConverter holds some converting functions of common types for internal usage. +type CommonTypeConverter struct { + Int64 func(v any) (int64, error) + Uint64 func(v any) (uint64, error) + String func(v any) (string, error) + Float32 func(v any) (float32, error) + Float64 func(v any) (float64, error) + Time func(v any, format ...string) (time.Time, error) + GTime func(v any, format ...string) (*gtime.Time, error) + Bytes func(v any) ([]byte, error) + Bool func(v any) (bool, error) +} + +// NewConvertConfig creates and returns a new ConvertConfig object. +func NewConvertConfig() *ConvertConfig { + return &ConvertConfig{ + cachedStructsInfoMap: sync.Map{}, + customConvertTypeMap: make(map[reflect.Type]struct{}), + anyToTypeConvertMap: make(map[reflect.Type]AnyConvertFunc), + } +} // RegisterCustomConvertType registers custom -func RegisterCustomConvertType(fieldType reflect.Type) { +func (cf *ConvertConfig) RegisterCustomConvertType(fieldType reflect.Type) { if fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() } - customConvertTypeMap[fieldType] = struct{}{} + cf.customConvertTypeMap[fieldType] = struct{}{} +} + +// RegisterAnyConvertFunc registers custom type converting function for specified type. +func (cf *ConvertConfig) RegisterAnyConvertFunc(t reflect.Type, convertFunc AnyConvertFunc) { + cf.anyToTypeConvertMap[t] = convertFunc } var ( diff --git a/util/gconv/internal/structcache/structcache_cached.go b/util/gconv/internal/structcache/structcache_cached.go index 1c7d4cf32..ef092f292 100644 --- a/util/gconv/internal/structcache/structcache_cached.go +++ b/util/gconv/internal/structcache/structcache_cached.go @@ -8,48 +8,19 @@ package structcache import ( "reflect" - "sync" - "time" "github.com/gogf/gf/v2/internal/utils" - "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gtag" ) -// CommonConverter holds some converting functions of common types for internal usage. -type CommonConverter struct { - Int64 func(any interface{}) int64 - Uint64 func(any interface{}) uint64 - String func(any interface{}) string - Float32 func(any interface{}) float32 - Float64 func(any interface{}) float64 - Time func(any interface{}, format ...string) time.Time - GTime func(any interface{}, format ...string) *gtime.Time - Bytes func(any interface{}) []byte - Bool func(any interface{}) bool -} - -var ( - // map[reflect.Type]*CachedStructInfo - cachedStructsInfoMap = sync.Map{} - - // localCommonConverter holds some converting functions of common types for internal usage. - localCommonConverter CommonConverter -) - -// RegisterCommonConverter registers the CommonConverter for local usage. -func RegisterCommonConverter(commonConverter CommonConverter) { - localCommonConverter = commonConverter -} - // GetCachedStructInfo retrieves or parses and returns a cached info for certain struct type. // The given `structType` should be type of struct. -func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStructInfo { +func (cf *ConvertConfig) GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStructInfo { if structType.Kind() != reflect.Struct { return nil } // check if it has been cached. - cachedStructInfo, ok := getCachedConvertStructInfo(structType) + cachedStructInfo, ok := cf.getCachedConvertStructInfo(structType) if ok { // directly returns the cached struct info if already exists. return cachedStructInfo @@ -58,9 +29,7 @@ func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStr // else create one. // it parses and generates a cache info for given struct type. - cachedStructInfo = &CachedStructInfo{ - tagOrFiledNameToFieldInfoMap: make(map[string]*CachedFieldInfo), - } + cachedStructInfo = NewCachedStructInfo(cf.customConvertTypeMap, cf.anyToTypeConvertMap) var ( priorityTagArray []string parentIndex = make([]int, 0) @@ -70,19 +39,19 @@ func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStr } else { priorityTagArray = gtag.StructTagPriority } - parseStructToCachedStructInfo(structType, parentIndex, cachedStructInfo, priorityTagArray) - storeCachedStructInfo(structType, cachedStructInfo) + cf.parseStructToCachedStructInfo(structType, parentIndex, cachedStructInfo, priorityTagArray) + cf.storeCachedStructInfo(structType, cachedStructInfo) return cachedStructInfo } -func storeCachedStructInfo(structType reflect.Type, cachedStructInfo *CachedStructInfo) { +func (cf *ConvertConfig) storeCachedStructInfo(structType reflect.Type, cachedStructInfo *CachedStructInfo) { // Temporarily enabled as an experimental feature - cachedStructsInfoMap.Store(structType, cachedStructInfo) + cf.cachedStructsInfoMap.Store(structType, cachedStructInfo) } -func getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, bool) { +func (cf *ConvertConfig) getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, bool) { // Temporarily enabled as an experimental feature - v, ok := cachedStructsInfoMap.Load(structType) + v, ok := cf.cachedStructsInfoMap.Load(structType) if ok { return v.(*CachedStructInfo), ok } @@ -91,7 +60,7 @@ func getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, boo // parseStructToCachedStructInfo parses given struct reflection type and stores its fields info into given CachedStructInfo. // It stores nothing into CachedStructInfo if given struct reflection type has no fields. -func parseStructToCachedStructInfo( +func (cf *ConvertConfig) parseStructToCachedStructInfo( structType reflect.Type, fieldIndexes []int, cachedStructInfo *CachedStructInfo, @@ -136,7 +105,7 @@ func parseStructToCachedStructInfo( // Do not add anonymous structures without tags cachedStructInfo.AddField(structField, append(copyFieldIndexes, i), priorityTagArray) } - parseStructToCachedStructInfo(fieldType, append(copyFieldIndexes, i), cachedStructInfo, priorityTagArray) + cf.parseStructToCachedStructInfo(fieldType, append(copyFieldIndexes, i), cachedStructInfo, priorityTagArray) continue } // Do not directly use append(fieldIndexes, i) diff --git a/util/gconv/internal/structcache/structcache_cached_field_info.go b/util/gconv/internal/structcache/structcache_cached_field_info.go index d50028ace..ff89e15cf 100644 --- a/util/gconv/internal/structcache/structcache_cached_field_info.go +++ b/util/gconv/internal/structcache/structcache_cached_field_info.go @@ -76,7 +76,7 @@ type CachedFieldInfoBase struct { OtherSameNameField []*CachedFieldInfo // ConvertFunc is the converting function for this field. - ConvertFunc func(from any, to reflect.Value) + ConvertFunc AnyConvertFunc // The last fuzzy matching key for this field. // The fuzzy matching occurs only if there are no direct tag and field name matching in the params map. diff --git a/util/gconv/internal/structcache/structcache_cached_struct_info.go b/util/gconv/internal/structcache/structcache_cached_struct_info.go index 245107376..e1e7e4084 100644 --- a/util/gconv/internal/structcache/structcache_cached_struct_info.go +++ b/util/gconv/internal/structcache/structcache_cached_struct_info.go @@ -9,14 +9,24 @@ package structcache import ( "reflect" "strings" - "time" "github.com/gogf/gf/v2/internal/utils" - "github.com/gogf/gf/v2/os/gtime" ) // CachedStructInfo holds the cached info for certain struct. type CachedStructInfo struct { + // All sub attributes field info slice. + fieldConvertInfos []*CachedFieldInfo + + // customConvertTypeMap is used to store whether field types are registered to custom conversions + // For example: + // func (src *TypeA) (dst *TypeB,err error) + // This map will store `TypeB` for quick judgment during assignment. + customConvertTypeMap map[reflect.Type]struct{} + + // anyToTypeConvertMap for custom type converting from any to its reflect.Value. + anyToTypeConvertMap map[reflect.Type]AnyConvertFunc + // This map field is mainly used in the bindStructWithLoopParamsMap method // key = field's name // Will save all field names and PriorityTagAndFieldName @@ -25,9 +35,23 @@ type CachedStructInfo struct { // // It will be stored twice, which keys are `name` and `field`. tagOrFiledNameToFieldInfoMap map[string]*CachedFieldInfo +} - // All sub attributes field info slice. - FieldConvertInfos []*CachedFieldInfo +// NewCachedStructInfo creates and returns a new CachedStructInfo object. +func NewCachedStructInfo( + customConvertTypeMap map[reflect.Type]struct{}, + anyToTypeConvertMap map[reflect.Type]AnyConvertFunc, +) *CachedStructInfo { + return &CachedStructInfo{ + tagOrFiledNameToFieldInfoMap: make(map[string]*CachedFieldInfo), + fieldConvertInfos: make([]*CachedFieldInfo, 0), + customConvertTypeMap: customConvertTypeMap, + anyToTypeConvertMap: anyToTypeConvertMap, + } +} + +func (csi *CachedStructInfo) GetFieldConvertInfos() []*CachedFieldInfo { + return csi.fieldConvertInfos } func (csi *CachedStructInfo) HasNoFields() bool { @@ -46,7 +70,7 @@ func (csi *CachedStructInfo) AddField(field reflect.StructField, fieldIndexes [] field, fieldIndexes, priorityTags, cachedFieldInfo, tagOrFieldName, ) if newFieldInfo.IsField { - csi.FieldConvertInfos = append(csi.FieldConvertInfos, newFieldInfo) + csi.fieldConvertInfos = append(csi.fieldConvertInfos, newFieldInfo) } // if the field info by `tagOrFieldName` already cached, // it so adds this new field info to other same name field. @@ -82,7 +106,9 @@ func (csi *CachedStructInfo) makeOrCopyCachedInfo( // copyCachedInfoWithFieldIndexes copies and returns a new CachedFieldInfo based on given CachedFieldInfo, but different // FieldIndexes. Mainly used for copying fields with the same name and type. -func (csi *CachedStructInfo) copyCachedInfoWithFieldIndexes(cfi *CachedFieldInfo, fieldIndexes []int) *CachedFieldInfo { +func (csi *CachedStructInfo) copyCachedInfoWithFieldIndexes( + cfi *CachedFieldInfo, fieldIndexes []int, +) *CachedFieldInfo { base := CachedFieldInfoBase{} base = *cfi.CachedFieldInfoBase base.FieldIndexes = fieldIndexes @@ -98,7 +124,7 @@ func (csi *CachedStructInfo) makeCachedFieldInfo( IsCommonInterface: checkTypeIsCommonInterface(field), StructField: field, FieldIndexes: fieldIndexes, - ConvertFunc: csi.genFieldConvertFunc(field.Type.String()), + ConvertFunc: csi.genFieldConvertFunc(field.Type), IsCustomConvert: csi.checkTypeHasCustomConvert(field.Type), PriorityTagAndFieldName: csi.genPriorityTagAndFieldName(field, priorityTags), RemoveSymbolsFieldName: utils.RemoveSymbols(field.Name), @@ -109,59 +135,20 @@ func (csi *CachedStructInfo) makeCachedFieldInfo( } } -func (csi *CachedStructInfo) genFieldConvertFunc(fieldType string) (convertFunc func(from any, to reflect.Value)) { - if fieldType[0] == '*' { - convertFunc = csi.genFieldConvertFunc(fieldType[1:]) +func (csi *CachedStructInfo) genFieldConvertFunc(fieldType reflect.Type) (convertFunc AnyConvertFunc) { + if v := csi.anyToTypeConvertMap[fieldType]; v != nil { + return v + } + + var fieldTypeKind = fieldType.Kind() + if fieldTypeKind == reflect.Ptr { + convertFunc = csi.genFieldConvertFunc(fieldType.Elem()) if convertFunc == nil { return nil } return csi.genPtrConvertFunc(convertFunc) } - switch fieldType { - case "int", "int8", "int16", "int32", "int64": - convertFunc = func(from any, to reflect.Value) { - to.SetInt(localCommonConverter.Int64(from)) - } - case "uint", "uint8", "uint16", "uint32", "uint64": - convertFunc = func(from any, to reflect.Value) { - to.SetUint(localCommonConverter.Uint64(from)) - } - case "string": - convertFunc = func(from any, to reflect.Value) { - to.SetString(localCommonConverter.String(from)) - } - case "float32": - convertFunc = func(from any, to reflect.Value) { - to.SetFloat(float64(localCommonConverter.Float32(from))) - } - case "float64": - convertFunc = func(from any, to reflect.Value) { - to.SetFloat(localCommonConverter.Float64(from)) - } - case "Time", "time.Time": - convertFunc = func(from any, to reflect.Value) { - *to.Addr().Interface().(*time.Time) = localCommonConverter.Time(from) - } - case "GTime", "gtime.Time": - convertFunc = func(from any, to reflect.Value) { - v := localCommonConverter.GTime(from) - if v == nil { - v = gtime.New() - } - *to.Addr().Interface().(*gtime.Time) = *v - } - case "bool": - convertFunc = func(from any, to reflect.Value) { - to.SetBool(localCommonConverter.Bool(from)) - } - case "[]byte": - convertFunc = func(from any, to reflect.Value) { - to.SetBytes(localCommonConverter.Bytes(from)) - } - default: - return nil - } - return convertFunc + return nil } func (csi *CachedStructInfo) genPriorityTagAndFieldName( @@ -188,21 +175,19 @@ func (csi *CachedStructInfo) genPriorityTagAndFieldName( return } +func (csi *CachedStructInfo) genPtrConvertFunc(convertFunc AnyConvertFunc) AnyConvertFunc { + return func(from any, to reflect.Value) error { + if to.IsNil() { + to.Set(reflect.New(to.Type().Elem())) + } + return convertFunc(from, to.Elem()) + } +} + func (csi *CachedStructInfo) checkTypeHasCustomConvert(fieldType reflect.Type) bool { if fieldType.Kind() == reflect.Ptr { fieldType = fieldType.Elem() } - _, ok := customConvertTypeMap[fieldType] + _, ok := csi.customConvertTypeMap[fieldType] return ok } - -func (csi *CachedStructInfo) genPtrConvertFunc( - convertFunc func(from any, to reflect.Value), -) func(from any, to reflect.Value) { - return func(from any, to reflect.Value) { - if to.IsNil() { - to.Set(reflect.New(to.Type().Elem())) - } - convertFunc(from, to.Elem()) - } -}