mirror of
https://github.com/duke-git/lancet.git
synced 2025-09-26 19:41:20 +08:00
217 lines
4.3 KiB
Go
217 lines
4.3 KiB
Go
// Copyright 2023 dudaodong@gmail.com. All rights reserved.
|
|
// Use of this source code is governed by MIT license
|
|
|
|
// Package convertor implements some functions to convert data.
|
|
package convertor
|
|
|
|
import "reflect"
|
|
|
|
type cloner struct {
|
|
ptrs map[reflect.Type]map[uintptr]reflect.Value
|
|
}
|
|
|
|
// clone return a duplicate of passed item.
|
|
func (c *cloner) clone(v reflect.Value) reflect.Value {
|
|
switch v.Kind() {
|
|
case reflect.Invalid:
|
|
return reflect.ValueOf(nil)
|
|
|
|
// bool
|
|
case reflect.Bool:
|
|
return reflect.ValueOf(v.Bool())
|
|
|
|
//int
|
|
case reflect.Int:
|
|
return reflect.ValueOf(int(v.Int()))
|
|
case reflect.Int8:
|
|
return reflect.ValueOf(int8(v.Int()))
|
|
case reflect.Int16:
|
|
return reflect.ValueOf(int16(v.Int()))
|
|
case reflect.Int32:
|
|
return reflect.ValueOf(int32(v.Int()))
|
|
case reflect.Int64:
|
|
return reflect.ValueOf(v.Int())
|
|
|
|
// uint
|
|
case reflect.Uint:
|
|
return reflect.ValueOf(uint(v.Uint()))
|
|
case reflect.Uint8:
|
|
return reflect.ValueOf(uint8(v.Uint()))
|
|
case reflect.Uint16:
|
|
return reflect.ValueOf(uint16(v.Uint()))
|
|
case reflect.Uint32:
|
|
return reflect.ValueOf(uint32(v.Uint()))
|
|
case reflect.Uint64:
|
|
return reflect.ValueOf(v.Uint())
|
|
|
|
// float
|
|
case reflect.Float32:
|
|
return reflect.ValueOf(float32(v.Float()))
|
|
case reflect.Float64:
|
|
return reflect.ValueOf(v.Float())
|
|
|
|
// complex
|
|
case reflect.Complex64:
|
|
return reflect.ValueOf(complex64(v.Complex()))
|
|
case reflect.Complex128:
|
|
return reflect.ValueOf(v.Complex())
|
|
|
|
// string
|
|
case reflect.String:
|
|
return reflect.ValueOf(v.String())
|
|
|
|
// array
|
|
case reflect.Array, reflect.Slice:
|
|
return c.cloneArray(v)
|
|
|
|
// map
|
|
case reflect.Map:
|
|
return c.cloneMap(v)
|
|
|
|
// Ptr
|
|
case reflect.Ptr:
|
|
return c.clonePtr(v)
|
|
|
|
// struct
|
|
case reflect.Struct:
|
|
return c.cloneStruct(v)
|
|
|
|
// func
|
|
case reflect.Func:
|
|
return v
|
|
|
|
// interface
|
|
case reflect.Interface:
|
|
return c.clone(v.Elem())
|
|
|
|
}
|
|
|
|
return reflect.Zero(v.Type())
|
|
}
|
|
|
|
func (c *cloner) cloneArray(v reflect.Value) reflect.Value {
|
|
if v.IsNil() {
|
|
return reflect.Zero(v.Type())
|
|
}
|
|
|
|
arr := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
|
|
|
|
for i := 0; i < v.Len(); i++ {
|
|
val := c.clone(v.Index(i))
|
|
|
|
if !val.IsValid() {
|
|
continue
|
|
}
|
|
|
|
item := arr.Index(i)
|
|
if !item.CanSet() {
|
|
continue
|
|
}
|
|
|
|
item.Set(val.Convert(item.Type()))
|
|
}
|
|
|
|
return arr
|
|
}
|
|
|
|
func (c *cloner) cloneMap(v reflect.Value) reflect.Value {
|
|
if v.IsNil() {
|
|
return reflect.Zero(v.Type())
|
|
}
|
|
|
|
clonedMap := reflect.MakeMap(v.Type())
|
|
|
|
for _, key := range v.MapKeys() {
|
|
value := v.MapIndex(key)
|
|
clonedKey := c.clone(key)
|
|
clonedValue := c.clone(value)
|
|
|
|
if !isNillable(clonedKey) || !clonedKey.IsNil() {
|
|
clonedKey = clonedKey.Convert(key.Type())
|
|
}
|
|
|
|
if (!isNillable(clonedValue) || !clonedValue.IsNil()) && clonedValue.IsValid() {
|
|
clonedValue = clonedValue.Convert(value.Type())
|
|
}
|
|
|
|
if !clonedValue.IsValid() {
|
|
clonedValue = reflect.Zero(clonedMap.Type().Elem())
|
|
}
|
|
|
|
clonedMap.SetMapIndex(clonedKey, clonedValue)
|
|
}
|
|
|
|
return clonedMap
|
|
}
|
|
|
|
func isNillable(v reflect.Value) bool {
|
|
switch v.Kind() {
|
|
case reflect.Chan, reflect.Interface, reflect.Ptr, reflect.Func:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *cloner) clonePtr(v reflect.Value) reflect.Value {
|
|
if v.IsNil() {
|
|
return reflect.Zero(v.Type())
|
|
}
|
|
|
|
var newVal reflect.Value
|
|
|
|
if v.Elem().CanAddr() {
|
|
ptrs, exists := c.ptrs[v.Type()]
|
|
if exists {
|
|
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
|
|
return newVal
|
|
}
|
|
}
|
|
}
|
|
|
|
newVal = c.clone(v.Elem())
|
|
|
|
if v.Elem().CanAddr() {
|
|
ptrs, exists := c.ptrs[v.Type()]
|
|
if exists {
|
|
if newVal, exists := ptrs[v.Elem().UnsafeAddr()]; exists {
|
|
return newVal
|
|
}
|
|
}
|
|
}
|
|
|
|
clonedPtr := reflect.New(newVal.Type())
|
|
clonedPtr.Elem().Set(newVal)
|
|
|
|
return clonedPtr
|
|
}
|
|
|
|
func (c *cloner) cloneStruct(v reflect.Value) reflect.Value {
|
|
clonedStructPtr := reflect.New(v.Type())
|
|
clonedStruct := clonedStructPtr.Elem()
|
|
|
|
if v.CanAddr() {
|
|
ptrs := c.ptrs[clonedStructPtr.Type()]
|
|
if ptrs == nil {
|
|
ptrs = make(map[uintptr]reflect.Value)
|
|
c.ptrs[clonedStructPtr.Type()] = ptrs
|
|
}
|
|
ptrs[v.UnsafeAddr()] = clonedStructPtr
|
|
}
|
|
|
|
for i := 0; i < v.NumField(); i++ {
|
|
newStructValue := clonedStruct.Field(i)
|
|
if !newStructValue.CanSet() {
|
|
continue
|
|
}
|
|
|
|
clonedVal := c.clone(v.Field(i))
|
|
if !clonedVal.IsValid() {
|
|
continue
|
|
}
|
|
|
|
newStructValue.Set(clonedVal.Convert(newStructValue.Type()))
|
|
}
|
|
|
|
return clonedStruct
|
|
}
|