spf13--viper/internal/convert/convert.go
2023-09-22 14:36:34 +08:00

247 lines
6 KiB
Go

package convert
import (
"fmt"
"reflect"
"strings"
)
var convertUtils = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
reflect.String: converNormal,
reflect.Int: converNormal,
reflect.Int16: converNormal,
reflect.Int32: converNormal,
reflect.Int64: converNormal,
reflect.Uint: converNormal,
reflect.Uint16: converNormal,
reflect.Uint32: converNormal,
reflect.Uint64: converNormal,
reflect.Float32: converNormal,
reflect.Float64: converNormal,
reflect.Uint8: converNormal,
reflect.Int8: converNormal,
reflect.Bool: converNormal,
}
//Convert 类型强制转换
//示例
/*
type Target struct {
A int `json:"aint"`
B string `json:"bstr"`
}
src :=map[string]interface{}{
"aint":1224,
"bstr":"124132"
}
var t Target
Convert(src,&t)
*/
//fix循环引用的问题
var _ = func() struct{} {
convertUtils[reflect.Map] = convertMap
convertUtils[reflect.Array] = convertSlice
convertUtils[reflect.Slice] = convertSlice
return struct{}{}
}()
func Convert(src interface{}, dst interface{}) (err error) {
defer func() {
if v := recover(); v != nil {
err = fmt.Errorf("panic recover:%v", v)
}
}()
dstRef := reflect.ValueOf(dst)
if dstRef.Kind() != reflect.Ptr {
return fmt.Errorf("dst is not ptr")
}
dstRef = reflect.Indirect(dstRef)
srcRef := reflect.ValueOf(src)
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
srcRef = srcRef.Elem()
}
if f, ok := convertUtils[srcRef.Kind()]; ok {
return f(srcRef, dstRef)
}
return fmt.Errorf("no implemented:%s", srcRef.Type())
}
func converNormal(src reflect.Value, dst reflect.Value) error {
if dst.CanSet() {
if src.Type() == dst.Type() {
dst.Set(src)
} else if src.CanConvert(dst.Type()) {
dst.Set(src.Convert(dst.Type()))
} else {
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
}
}
return nil
}
func convertSlice(src reflect.Value, dst reflect.Value) error {
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
return fmt.Errorf("error type:%s", dst.Type().String())
} else if !src.IsValid() {
return nil
}
l := src.Len()
target := reflect.MakeSlice(dst.Type(), l, l)
if dst.CanSet() {
dst.Set(target)
}
for i := 0; i < l; i++ {
srcValue := src.Index(i)
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
srcValue = srcValue.Elem()
}
if f, ok := convertUtils[srcValue.Kind()]; ok {
err := f(srcValue, dst.Index(i))
if err != nil {
return err
}
}
}
return nil
}
func convertMap(src reflect.Value, dst reflect.Value) error {
//
if src.Kind() == reflect.Ptr || src.Kind() == reflect.Interface {
src = src.Elem()
}
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
if dst.Kind() == reflect.Map {
return converMapToMap(src, dst)
}
if !(dst.Kind() == reflect.Ptr && dst.Type().Elem().Kind() == reflect.Struct) {
if dst.Kind() == reflect.Interface && dst.CanSet() {
dst.Set(src)
return nil
}
return fmt.Errorf("src or dst type error:%s,%s", src.Kind().String(), dst.Type().String())
}
if !reflect.Indirect(dst).IsValid() {
v := reflect.New(dst.Type().Elem())
dst.Set(v)
}
dst = reflect.Indirect(dst)
}
dstType := dst.Type()
num := dstType.NumField()
exist := map[string]int{}
for i := 0; i < num; i++ {
k := dstType.Field(i).Tag.Get("viper")
if k == "" {
k = dstType.Field(i).Name
}
if strings.Contains(k, ",") {
taglist := strings.Split(k, ",")
if taglist[0] == "" {
if len(taglist) == 2 &&
taglist[1] == "inline" {
v := dst.Field(i)
err := convertMap(src, v)
if err != nil {
return err
}
dst.Field(i).Set(v)
continue
} else {
k = dstType.Field(i).Name
}
} else {
k = taglist[0]
}
}
exist[k] = i
}
keys := src.MapKeys()
for _, key := range keys {
if index, ok := exist[key.String()]; ok {
v := dst.Field(index)
if v.Kind() == reflect.Struct {
err := convertMap(src.MapIndex(key), v)
if err != nil {
return err
}
} else if v.Kind() == reflect.Slice {
err := convertSlice(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
if v.CanSet() && src.MapIndex(key).IsValid() && !src.MapIndex(key).IsZero() {
if v.Type() == src.MapIndex(key).Elem().Type() {
v.Set(src.MapIndex(key).Elem())
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
} else if f, ok := convertUtils[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
err := f(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
} else {
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.MapIndex(key).Elem().Type())
}
}
}
}
}
return nil
}
func converMapToMap(src reflect.Value, dst reflect.Value) error {
if src.Kind() != reflect.Map || dst.Kind() != reflect.Map {
return fmt.Errorf("type error: src(%v),dst(%v)", src.Kind(), src.Kind())
}
mv := reflect.MakeMap(dst.Type())
keys := src.MapKeys()
dt := dst.Type().Elem().Kind()
for _, key := range keys {
if dt == reflect.Struct {
me := reflect.New(dst.Type().Elem())
me = reflect.Indirect(me)
convertMap(src.MapIndex(key).Elem(), me)
mv.SetMapIndex(key, me)
} else if dt == reflect.Ptr {
me := reflect.New(dst.Type().Elem().Elem())
me = reflect.Indirect(me)
convertMap(src.MapIndex(key).Elem(), me)
mv.SetMapIndex(key, me.Addr())
} else if dt == reflect.Slice {
l := src.MapIndex(key).Elem().Len()
v := reflect.MakeSlice(dst.Type().Elem(), l, l)
err := convertSlice(src.MapIndex(key).Elem(), v)
if err != nil {
return err
}
mv.SetMapIndex(key, v)
} else {
if src.MapIndex(key).Elem().Kind() != dst.Type().Elem().Kind() &&
src.MapIndex(key).Elem().CanConvert(dst.Type().Elem()) {
v := src.MapIndex(key).Elem().Convert(dst.Type().Elem())
mv.SetMapIndex(key, v)
continue
}
mv.SetMapIndex(key, src.MapIndex(key).Elem())
}
}
dst.Set(mv)
return nil
}