mirror of
https://gitee.com/johng/gf
synced 2026-07-04 13:02:36 +08:00
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
620 lines
16 KiB
Go
620 lines
16 KiB
Go
// 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 converter
|
|
|
|
import (
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/internal/json"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
)
|
|
|
|
// ConvertOption is the option for converting.
|
|
type ConvertOption struct {
|
|
// ExtraParams are extra values for implementing the converting.
|
|
ExtraParams []any
|
|
SliceOption SliceOption
|
|
MapOption MapOption
|
|
StructOption StructOption
|
|
}
|
|
|
|
func (c *Converter) getConvertOption(option ...ConvertOption) ConvertOption {
|
|
if len(option) > 0 {
|
|
return option[0]
|
|
}
|
|
return ConvertOption{}
|
|
}
|
|
|
|
// ConvertWithTypeName converts the variable `fromValue` to the type `toTypeName`, the type `toTypeName` is specified by string.
|
|
func (c *Converter) ConvertWithTypeName(fromValue any, toTypeName string, option ...ConvertOption) (any, error) {
|
|
return c.doConvert(
|
|
doConvertInput{
|
|
FromValue: fromValue,
|
|
ToTypeName: toTypeName,
|
|
ReferValue: nil,
|
|
},
|
|
c.getConvertOption(option...),
|
|
)
|
|
}
|
|
|
|
// ConvertWithRefer converts the variable `fromValue` to the type referred by value `referValue`.
|
|
func (c *Converter) ConvertWithRefer(fromValue, referValue any, option ...ConvertOption) (any, error) {
|
|
var referValueRf reflect.Value
|
|
if v, ok := referValue.(reflect.Value); ok {
|
|
referValueRf = v
|
|
} else {
|
|
referValueRf = reflect.ValueOf(referValue)
|
|
}
|
|
return c.doConvert(
|
|
doConvertInput{
|
|
FromValue: fromValue,
|
|
ToTypeName: referValueRf.Type().String(),
|
|
ReferValue: referValue,
|
|
},
|
|
c.getConvertOption(option...),
|
|
)
|
|
}
|
|
|
|
type doConvertInput struct {
|
|
FromValue any // Value that is converted from.
|
|
ToTypeName string // Target value type name in string.
|
|
ReferValue any // Referred value, a value in type `ToTypeName`. Note that its type might be reflect.Value.
|
|
|
|
// 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 (c *Converter) doConvert(in doConvertInput, option ConvertOption) (convertedValue any, err error) {
|
|
switch in.ToTypeName {
|
|
case "int":
|
|
return c.Int(in.FromValue)
|
|
case "*int":
|
|
if _, ok := in.FromValue.(*int); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int8":
|
|
return c.Int8(in.FromValue)
|
|
case "*int8":
|
|
if _, ok := in.FromValue.(*int8); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int8(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int16":
|
|
return c.Int16(in.FromValue)
|
|
case "*int16":
|
|
if _, ok := in.FromValue.(*int16); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int16(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int32":
|
|
return c.Int32(in.FromValue)
|
|
case "*int32":
|
|
if _, ok := in.FromValue.(*int32); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int32(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "int64":
|
|
return c.Int64(in.FromValue)
|
|
case "*int64":
|
|
if _, ok := in.FromValue.(*int64); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Int64(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint":
|
|
return c.Uint(in.FromValue)
|
|
case "*uint":
|
|
if _, ok := in.FromValue.(*uint); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint8":
|
|
return c.Uint8(in.FromValue)
|
|
case "*uint8":
|
|
if _, ok := in.FromValue.(*uint8); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint8(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint16":
|
|
return c.Uint16(in.FromValue)
|
|
case "*uint16":
|
|
if _, ok := in.FromValue.(*uint16); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint16(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint32":
|
|
return c.Uint32(in.FromValue)
|
|
case "*uint32":
|
|
if _, ok := in.FromValue.(*uint32); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint32(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "uint64":
|
|
return c.Uint64(in.FromValue)
|
|
case "*uint64":
|
|
if _, ok := in.FromValue.(*uint64); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Uint64(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "float32":
|
|
return c.Float32(in.FromValue)
|
|
case "*float32":
|
|
if _, ok := in.FromValue.(*float32); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Float32(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "float64":
|
|
return c.Float64(in.FromValue)
|
|
case "*float64":
|
|
if _, ok := in.FromValue.(*float64); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Float64(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "bool":
|
|
return c.Bool(in.FromValue)
|
|
case "*bool":
|
|
if _, ok := in.FromValue.(*bool); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Bool(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "string":
|
|
return c.String(in.FromValue)
|
|
case "*string":
|
|
if _, ok := in.FromValue.(*string); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.String(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "[]byte":
|
|
return c.Bytes(in.FromValue)
|
|
case "[]int":
|
|
return c.SliceInt(in.FromValue, option.SliceOption)
|
|
case "[]int32":
|
|
return c.SliceInt32(in.FromValue, option.SliceOption)
|
|
case "[]int64":
|
|
return c.SliceInt64(in.FromValue, option.SliceOption)
|
|
case "[]uint":
|
|
return c.SliceUint(in.FromValue, option.SliceOption)
|
|
case "[]uint8":
|
|
return c.Bytes(in.FromValue)
|
|
case "[]uint32":
|
|
return c.SliceUint32(in.FromValue, option.SliceOption)
|
|
case "[]uint64":
|
|
return c.SliceUint64(in.FromValue, option.SliceOption)
|
|
case "[]float32":
|
|
return c.SliceFloat32(in.FromValue, option.SliceOption)
|
|
case "[]float64":
|
|
return c.SliceFloat64(in.FromValue, option.SliceOption)
|
|
case "[]string":
|
|
return c.SliceStr(in.FromValue, option.SliceOption)
|
|
|
|
case "Time", "time.Time":
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.Time(in.FromValue, s)
|
|
}
|
|
return c.Time(in.FromValue)
|
|
case "*time.Time":
|
|
var v time.Time
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
v, err = c.Time(in.FromValue, s)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
} else {
|
|
if _, ok := in.FromValue.(*time.Time); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err = c.Time(in.FromValue)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
}
|
|
return &v, nil
|
|
|
|
case "GTime", "gtime.Time":
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return *gtime.New(), err
|
|
}
|
|
v, err := c.GTime(in.FromValue, s)
|
|
if err != nil {
|
|
return *gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return *v, nil
|
|
}
|
|
return *gtime.New(), nil
|
|
}
|
|
v, err := c.GTime(in.FromValue)
|
|
if err != nil {
|
|
return *gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return *v, nil
|
|
}
|
|
return *gtime.New(), nil
|
|
case "*gtime.Time":
|
|
if len(option.ExtraParams) > 0 {
|
|
s, err := c.String(option.ExtraParams[0])
|
|
if err != nil {
|
|
return gtime.New(), err
|
|
}
|
|
v, err := c.GTime(in.FromValue, s)
|
|
if err != nil {
|
|
return gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return v, nil
|
|
}
|
|
return gtime.New(), nil
|
|
}
|
|
v, err := c.GTime(in.FromValue)
|
|
if err != nil {
|
|
return gtime.New(), err
|
|
}
|
|
if v != nil {
|
|
return v, nil
|
|
}
|
|
return gtime.New(), nil
|
|
|
|
case "Duration", "time.Duration":
|
|
return c.Duration(in.FromValue)
|
|
case "*time.Duration":
|
|
if _, ok := in.FromValue.(*time.Duration); ok {
|
|
return in.FromValue, nil
|
|
}
|
|
v, err := c.Duration(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
|
|
case "map[string]string":
|
|
return c.MapStrStr(in.FromValue, option.MapOption)
|
|
|
|
case "map[string]interface {}":
|
|
return c.Map(in.FromValue, option.MapOption)
|
|
|
|
case "[]map[string]interface {}":
|
|
return c.SliceMap(in.FromValue, SliceMapOption{
|
|
SliceOption: option.SliceOption,
|
|
MapOption: option.MapOption,
|
|
})
|
|
|
|
case "RawMessage", "json.RawMessage":
|
|
// issue 3449
|
|
bytes, err := json.Marshal(in.FromValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bytes, nil
|
|
|
|
default:
|
|
return c.doConvertForDefault(in, option)
|
|
}
|
|
}
|
|
|
|
func (c *Converter) doConvertForDefault(in doConvertInput, option ConvertOption) (convertedValue any, err error) {
|
|
if in.ReferValue != nil {
|
|
var referReflectValue reflect.Value
|
|
if v, ok := in.ReferValue.(reflect.Value); ok {
|
|
referReflectValue = v
|
|
} else {
|
|
referReflectValue = reflect.ValueOf(in.ReferValue)
|
|
}
|
|
var fromReflectValue reflect.Value
|
|
if v, ok := in.FromValue.(reflect.Value); ok {
|
|
fromReflectValue = v
|
|
} else {
|
|
fromReflectValue = reflect.ValueOf(in.FromValue)
|
|
}
|
|
|
|
// custom converter.
|
|
dstReflectValue, ok, err := c.callCustomConverterWithRefer(fromReflectValue, referReflectValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return dstReflectValue.Interface(), nil
|
|
}
|
|
|
|
defer func() {
|
|
if recover() != nil {
|
|
in.alreadySetToReferValue = false
|
|
if err = c.bindVarToReflectValue(referReflectValue, in.FromValue, option.StructOption); err == nil {
|
|
in.alreadySetToReferValue = true
|
|
convertedValue = referReflectValue.Interface()
|
|
}
|
|
}
|
|
}()
|
|
switch referReflectValue.Kind() {
|
|
case reflect.Pointer:
|
|
// Type converting for custom type pointers.
|
|
// Eg:
|
|
// type PayMode int
|
|
// type Req struct{
|
|
// Mode *PayMode
|
|
// }
|
|
//
|
|
// Struct(`{"Mode": 1000}`, &req)
|
|
originType := referReflectValue.Type().Elem()
|
|
switch originType.Kind() {
|
|
case reflect.Struct:
|
|
// Not support some kinds.
|
|
default:
|
|
in.ToTypeName = originType.Kind().String()
|
|
in.ReferValue = nil
|
|
result, err := c.doConvert(in, option)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
refElementValue := reflect.ValueOf(result)
|
|
originTypeValue := reflect.New(refElementValue.Type()).Elem()
|
|
originTypeValue.Set(refElementValue)
|
|
in.alreadySetToReferValue = true
|
|
return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface(), nil
|
|
}
|
|
|
|
case reflect.Map:
|
|
var targetValue = reflect.New(referReflectValue.Type()).Elem()
|
|
if err = c.MapToMap(in.FromValue, targetValue, nil, option.MapOption); err == nil {
|
|
in.alreadySetToReferValue = true
|
|
}
|
|
return targetValue.Interface(), nil
|
|
|
|
default:
|
|
}
|
|
in.ToTypeName = referReflectValue.Kind().String()
|
|
in.ReferValue = nil
|
|
in.alreadySetToReferValue = true
|
|
result, err := c.doConvert(in, option)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
convertedValue = reflect.ValueOf(result).Convert(referReflectValue.Type()).Interface()
|
|
return convertedValue, nil
|
|
}
|
|
return in.FromValue, nil
|
|
}
|
|
|
|
func (c *Converter) doConvertWithReflectValueSet(reflectValue reflect.Value, in doConvertInput, option ConvertOption) error {
|
|
convertedValue, err := c.doConvert(in, option)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !in.alreadySetToReferValue {
|
|
reflectValue.Set(reflect.ValueOf(convertedValue))
|
|
}
|
|
return err
|
|
}
|
|
|
|
// callCustomConverter call the custom converter. It will try some possible type.
|
|
func (c *Converter) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
|
|
// search type converter function.
|
|
registeredConverterFunc, srcType, ok := c.getRegisteredTypeConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
|
|
if ok {
|
|
return c.doCallCustomTypeConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
|
}
|
|
|
|
// search any converter function.
|
|
anyConverterFunc := c.getRegisteredAnyConverterFunc(dstReflectValue)
|
|
if anyConverterFunc == nil {
|
|
return false, nil
|
|
}
|
|
err = anyConverterFunc(srcReflectValue.Interface(), dstReflectValue)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (c *Converter) callCustomConverterWithRefer(
|
|
srcReflectValue, referReflectValue reflect.Value,
|
|
) (dstReflectValue reflect.Value, converted bool, err error) {
|
|
// search type converter function.
|
|
registeredConverterFunc, srcType, ok := c.getRegisteredTypeConverterFuncAndSrcType(
|
|
srcReflectValue, referReflectValue,
|
|
)
|
|
if ok {
|
|
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
|
|
converted, err = c.doCallCustomTypeConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
|
return
|
|
}
|
|
|
|
// search any converter function.
|
|
anyConverterFunc := c.getRegisteredAnyConverterFunc(referReflectValue)
|
|
if anyConverterFunc == nil {
|
|
return reflect.Value{}, false, nil
|
|
}
|
|
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
|
|
err = anyConverterFunc(srcReflectValue.Interface(), dstReflectValue)
|
|
if err != nil {
|
|
return reflect.Value{}, false, err
|
|
}
|
|
return dstReflectValue, true, nil
|
|
}
|
|
|
|
func (c *Converter) getRegisteredTypeConverterFuncAndSrcType(
|
|
srcReflectValue, dstReflectValueForRefer reflect.Value,
|
|
) (f converterFunc, srcType reflect.Type, ok bool) {
|
|
if len(c.typeConverterFuncMap) == 0 {
|
|
return reflect.Value{}, nil, false
|
|
}
|
|
srcType = srcReflectValue.Type()
|
|
for srcType.Kind() == reflect.Pointer {
|
|
srcType = srcType.Elem()
|
|
}
|
|
var registeredOutTypeMap map[converterOutType]converterFunc
|
|
// firstly, it searches the map by input parameter type.
|
|
registeredOutTypeMap, ok = c.typeConverterFuncMap[srcType]
|
|
if !ok {
|
|
return reflect.Value{}, nil, false
|
|
}
|
|
var dstType = dstReflectValueForRefer.Type()
|
|
if dstType.Kind() == reflect.Pointer {
|
|
// Might be **struct, which is support as designed.
|
|
if dstType.Elem().Kind() == reflect.Pointer {
|
|
dstType = dstType.Elem()
|
|
}
|
|
} else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() {
|
|
dstType = dstReflectValueForRefer.Addr().Type()
|
|
} else {
|
|
dstType = reflect.PointerTo(dstType)
|
|
}
|
|
// secondly, it searches the input parameter type map
|
|
// and finds the result converter function by the output parameter type.
|
|
f, ok = registeredOutTypeMap[dstType]
|
|
if !ok {
|
|
return reflect.Value{}, nil, false
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *Converter) getRegisteredAnyConverterFunc(dstReflectValueForRefer reflect.Value) (f AnyConvertFunc) {
|
|
if c.internalConverter.IsAnyConvertFuncEmpty() {
|
|
return nil
|
|
}
|
|
if !dstReflectValueForRefer.IsValid() {
|
|
return nil
|
|
}
|
|
var dstType = dstReflectValueForRefer.Type()
|
|
if dstType.Kind() == reflect.Pointer {
|
|
// Might be **struct, which is support as designed.
|
|
if dstType.Elem().Kind() == reflect.Pointer {
|
|
dstType = dstType.Elem()
|
|
}
|
|
} else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() {
|
|
dstType = dstReflectValueForRefer.Addr().Type()
|
|
} else {
|
|
dstType = reflect.PointerTo(dstType)
|
|
}
|
|
return c.internalConverter.GetAnyConvertFuncByType(dstType)
|
|
}
|
|
|
|
func (c *Converter) doCallCustomTypeConverter(
|
|
srcReflectValue reflect.Value,
|
|
dstReflectValue reflect.Value,
|
|
registeredConverterFunc converterFunc,
|
|
srcType reflect.Type,
|
|
) (converted bool, err error) {
|
|
// Converter function calling.
|
|
for srcReflectValue.Type() != srcType {
|
|
srcReflectValue = srcReflectValue.Elem()
|
|
}
|
|
result := registeredConverterFunc.Call([]reflect.Value{srcReflectValue})
|
|
if !result[1].IsNil() {
|
|
return false, result[1].Interface().(error)
|
|
}
|
|
// The `result[0]` is a pointer.
|
|
if result[0].IsNil() {
|
|
return false, nil
|
|
}
|
|
var resultValue = result[0]
|
|
for {
|
|
if resultValue.Type() == dstReflectValue.Type() && dstReflectValue.CanSet() {
|
|
dstReflectValue.Set(resultValue)
|
|
converted = true
|
|
} else if dstReflectValue.Kind() == reflect.Pointer {
|
|
if resultValue.Type() == dstReflectValue.Elem().Type() && dstReflectValue.Elem().CanSet() {
|
|
dstReflectValue.Elem().Set(resultValue)
|
|
converted = true
|
|
}
|
|
}
|
|
if converted {
|
|
break
|
|
}
|
|
if resultValue.Kind() == reflect.Pointer {
|
|
resultValue = resultValue.Elem()
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return converted, nil
|
|
}
|