1
0
mirror of https://github.com/gogf/gf.git synced 2025-04-05 03:05:05 +08:00
gf/util/gconv/internal/converter/converter_convert.go

624 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.
var (
ok bool
dstReflectValue reflect.Value
)
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.Ptr:
// Type converting for custom type pointers.
// Example:
// 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
}