Skip to content

Commit

Permalink
up
Browse files Browse the repository at this point in the history
  • Loading branch information
gqcn committed Mar 2, 2025
1 parent 97c40a5 commit 7f03024
Show file tree
Hide file tree
Showing 12 changed files with 443 additions and 241 deletions.
20 changes: 6 additions & 14 deletions util/gconv/gconv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}{
Expand All @@ -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()
)
47 changes: 27 additions & 20 deletions util/gconv/gconv_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -40,26 +43,30 @@ 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 {
FromValue interface{} // Value that is converted from.
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)
Expand Down Expand Up @@ -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()
}
Expand All @@ -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
Expand All @@ -335,23 +342,23 @@ 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()
}
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))
}
Expand Down
99 changes: 99 additions & 0 deletions util/gconv/gconv_convert_config.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
46 changes: 21 additions & 25 deletions util/gconv/gconv_converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,22 @@ 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
)

// customConverters for internal converter storing.
var customConverters = make(map[converterInType]map[converterOutType]converterFunc)
// RegisterConverter registers custom converter.
func RegisterConverter(fn any) (err error) {
return defaultConvertConfig.RegisterConverter(fn)
}

// 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.
//
// Note:
// 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()
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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()
Expand All @@ -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
}
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 7f03024

Please sign in to comment.