1078 lines
24 KiB
Go
1078 lines
24 KiB
Go
package copier
|
||
|
||
import (
|
||
"fmt"
|
||
"reflect"
|
||
"slices"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
type copier struct {
|
||
opt *options
|
||
visited map[uintptr]struct{}
|
||
mu sync.Mutex
|
||
}
|
||
|
||
func New(opts ...option) *copier {
|
||
opt := getOpt(opts...)
|
||
var visited map[uintptr]struct{}
|
||
if opt.detectCircularRefs {
|
||
visited = make(map[uintptr]struct{})
|
||
}
|
||
|
||
return &copier{
|
||
visited: visited,
|
||
opt: opt,
|
||
}
|
||
}
|
||
|
||
func (c *copier) Copy(dst, src any) error {
|
||
c.mu.Lock()
|
||
defer c.reset()
|
||
defer c.mu.Unlock()
|
||
|
||
if dst == nil || src == nil {
|
||
return ErrInvalidCopyDestination
|
||
}
|
||
|
||
srcValue, dstValue := indirect(reflect.ValueOf(src)), indirect(reflect.ValueOf(dst))
|
||
|
||
if !srcValue.IsValid() {
|
||
return ErrInvalidCopyFrom
|
||
}
|
||
|
||
if !dstValue.CanSet() {
|
||
return ErrInvalidCopyDestination
|
||
}
|
||
|
||
return c.deepCopy(dstValue, srcValue, 0)
|
||
}
|
||
|
||
func (c *copier) reset() {
|
||
if c.opt.detectCircularRefs {
|
||
clear(c.visited)
|
||
}
|
||
}
|
||
|
||
func (c *copier) deepCopy(dst, src reflect.Value, depth int) error {
|
||
if c.ExceedMaxDepth(depth) {
|
||
return ErrMaxDepthExceeded
|
||
}
|
||
|
||
// 复制源为空值,直接返回
|
||
if !src.IsValid() || src.IsZero() {
|
||
return nil
|
||
}
|
||
|
||
if c.isCircularRefs(src) {
|
||
return ErrCircularReference
|
||
}
|
||
|
||
// 可以直接赋值时直接赋值并返回
|
||
srcType, dstType := src.Type(), dst.Type()
|
||
|
||
// 处理时间类型
|
||
if isTimeType(srcType) || isTimeType(dstType) {
|
||
return c.copyTime(dst, src)
|
||
}
|
||
|
||
// fmt.Println("srcType:", srcType, src.Kind().String(), "dstType:", dstType, dst.Kind().String())
|
||
|
||
switch src.Kind() {
|
||
case reflect.Slice, reflect.Array:
|
||
return c.copySliceOrArray(dst, src, depth)
|
||
case reflect.Map:
|
||
return c.copyMap(dst, src, depth)
|
||
case reflect.Struct:
|
||
return c.copyStruct(dst, src, depth)
|
||
case reflect.Pointer:
|
||
return c.copyPointer(dst, src, depth)
|
||
case reflect.Interface:
|
||
return c.copyInterface(dst, src, depth)
|
||
case reflect.Chan, reflect.Func, reflect.UnsafePointer:
|
||
return ErrNotSupported(srcType, dstType)
|
||
default:
|
||
return c.copyBasic(dst, src)
|
||
}
|
||
}
|
||
|
||
func (c *copier) copySliceOrArray(dst, src reflect.Value, depth int) error {
|
||
switch dst.Kind() {
|
||
case reflect.Slice, reflect.Array:
|
||
return c.copySliceToSlice(dst, src, depth)
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
}
|
||
|
||
func (c *copier) copyStructToSlice(dst, src reflect.Value, depth int) error {
|
||
if dst.Kind() != reflect.Slice {
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
// 创建新的slice,长度+1
|
||
dstLen := dst.Len()
|
||
newSlice := reflect.MakeSlice(dst.Type(), dstLen+1, dstLen+1)
|
||
|
||
// 复制原有元素
|
||
for i := range dstLen {
|
||
if err := c.deepCopy(newSlice.Index(i), dst.Index(i), depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// 追加新元素(结构体)
|
||
if err := c.deepCopy(newSlice.Index(dstLen), src, depth+1); err != nil {
|
||
return err
|
||
}
|
||
|
||
dst.Set(newSlice)
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) copySliceToSlice(dst, src reflect.Value, depth int) error {
|
||
if dst.Kind() == reflect.Array {
|
||
return c.copyToArray(dst, src, depth)
|
||
}
|
||
|
||
return c.copyToSlice(dst, src, depth)
|
||
}
|
||
|
||
func (c *copier) copyToSlice(dst, src reflect.Value, depth int) error {
|
||
srcLen := src.Len()
|
||
|
||
if dst.IsNil() || dst.Len() < srcLen {
|
||
newSlice := reflect.MakeSlice(dst.Type(), srcLen, srcLen)
|
||
dst.Set(newSlice)
|
||
}
|
||
|
||
copyLen := min(dst.Len(), srcLen)
|
||
for i := range copyLen {
|
||
if err := c.deepCopy(dst.Index(i), src.Index(i), depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) copyToArray(dst, src reflect.Value, depth int) error {
|
||
srcLen := src.Len()
|
||
dstLen := dst.Len()
|
||
|
||
// 取较小长度
|
||
copyLen := min(dstLen, srcLen)
|
||
for i := range copyLen {
|
||
if err := c.deepCopy(dst.Index(i), src.Index(i), depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) copyStructToMap(dst, src reflect.Value, depth int) error {
|
||
if dst.IsNil() {
|
||
dst.Set(reflect.MakeMapWithSize(dst.Type(), src.NumField()))
|
||
}
|
||
|
||
fields := c.deepFields(src.Type())
|
||
for _, sf := range fields {
|
||
if sf.Field.Anonymous {
|
||
switch sf.Field.Type.Kind() {
|
||
case reflect.Struct, reflect.Pointer:
|
||
field := c.fieldByName(src, sf.Field.Name)
|
||
if err := c.deepCopy(dst, field, depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
continue
|
||
}
|
||
|
||
tag := parseTag(sf.Field.Tag.Get(c.opt.tagName))
|
||
if tag.Contains(tagIgnore) {
|
||
continue
|
||
}
|
||
|
||
name := c.getFieldName(sf.Field.Name, tag)
|
||
|
||
sField := c.fieldByName(src, sf.Field.Name)
|
||
|
||
if c.opt.ignoreEmpty && sField.IsZero() {
|
||
continue
|
||
}
|
||
|
||
var copiedValue reflect.Value
|
||
if isTimeType(sField.Type()) {
|
||
copiedValue = reflect.New(sField.Type()).Elem()
|
||
if err := c.copyTime(copiedValue, sField); err != nil {
|
||
return err
|
||
}
|
||
} else if sField.Kind() == reflect.Struct {
|
||
copiedValue = reflect.ValueOf(make(map[string]any))
|
||
sField = indirect(sField)
|
||
if err := c.deepCopy(copiedValue, sField, depth+1); err != nil {
|
||
return err
|
||
}
|
||
} else {
|
||
copiedValue = sField
|
||
}
|
||
|
||
dst.SetMapIndex(reflect.ValueOf(name), copiedValue)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) copyMap(dst, src reflect.Value, depth int) error {
|
||
switch dst.Kind() {
|
||
case reflect.Map:
|
||
return c.copyMapToMap(dst, src, depth)
|
||
case reflect.Struct:
|
||
return c.copyMapToStruct(dst, src, depth)
|
||
case reflect.Pointer:
|
||
if dst.IsNil() {
|
||
elemType := dst.Type().Elem()
|
||
newPtr := reflect.New(elemType)
|
||
if err := c.deepCopy(newPtr.Elem(), src, depth+1); err != nil {
|
||
return err
|
||
}
|
||
dst.Set(newPtr)
|
||
} else {
|
||
if err := c.deepCopy(dst.Elem(), src, depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
}
|
||
|
||
func (c *copier) copyMapToMap(dst, src reflect.Value, depth int) error {
|
||
if dst.IsNil() {
|
||
dst.Set(reflect.MakeMapWithSize(dst.Type(), src.Len()))
|
||
}
|
||
|
||
dstType, _ := indirectType(dst.Type())
|
||
iter := src.MapRange()
|
||
|
||
for iter.Next() {
|
||
key := iter.Key()
|
||
value := iter.Value()
|
||
|
||
if c.opt.ignoreEmpty && value.IsZero() {
|
||
continue
|
||
}
|
||
|
||
if key.Kind() == reflect.Interface {
|
||
key = key.Elem()
|
||
}
|
||
|
||
if name, ok := key.Interface().(string); ok {
|
||
fieldName := c.getFieldName(name, nil)
|
||
key = reflect.ValueOf(fieldName)
|
||
}
|
||
|
||
copiedValue := c.prepareMapValue(dstType.Elem(), value)
|
||
if err := c.deepCopy(copiedValue, value, depth+1); err != nil {
|
||
return err
|
||
}
|
||
dst.SetMapIndex(key, copiedValue)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) prepareMapValue(elemType reflect.Type, value reflect.Value) reflect.Value {
|
||
if elemType.Kind() == reflect.Interface {
|
||
if value.Kind() == reflect.Interface {
|
||
return reflect.New(value.Elem().Type()).Elem()
|
||
}
|
||
return reflect.New(value.Type()).Elem()
|
||
}
|
||
return reflect.New(elemType).Elem()
|
||
}
|
||
|
||
func (c *copier) copyMapToStruct(dst, src reflect.Value, depth int) error {
|
||
fields := c.deepFields(dst.Type())
|
||
for _, sf := range fields {
|
||
if nestedAnonymousField(dst, sf.Field.Name) {
|
||
continue
|
||
}
|
||
|
||
field := dst.FieldByName(sf.Field.Name)
|
||
if !field.CanSet() {
|
||
continue
|
||
}
|
||
|
||
if sf.Field.Anonymous {
|
||
if err := c.deepCopy(field, src, depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
name := sf.Field.Name
|
||
var mapValue reflect.Value
|
||
if c.opt.caseSensitive {
|
||
mapValue = src.MapIndex(reflect.ValueOf(name))
|
||
} else {
|
||
// fix: 忽略大小写查询
|
||
mapValue = src.MapIndex(reflect.ValueOf(strings.ToLower(name)))
|
||
}
|
||
|
||
if !mapValue.IsValid() {
|
||
continue
|
||
}
|
||
|
||
if mapValue.Kind() == reflect.Interface {
|
||
mapValue = mapValue.Elem()
|
||
}
|
||
|
||
if err := c.deepCopy(field, mapValue, depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) copyStruct(dst, src reflect.Value, depth int) error {
|
||
switch dst.Kind() {
|
||
case reflect.Struct:
|
||
return c.copyStructToStruct(dst, src, depth)
|
||
case reflect.Map:
|
||
return c.copyStructToMap(dst, src, depth)
|
||
case reflect.Slice:
|
||
return c.copyStructToSlice(dst, src, depth)
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
}
|
||
|
||
func (c *copier) copyStructToStruct(dst, src reflect.Value, depth int) error {
|
||
if _, ok := src.Interface().(time.Time); ok && src.Type().AssignableTo(dst.Type()) {
|
||
dst.Set(src)
|
||
return nil
|
||
}
|
||
|
||
srcFields := c.deepFields(src.Type())
|
||
dstFields := c.deepFields(dst.Type())
|
||
|
||
for _, sf := range dstFields {
|
||
_ = sf
|
||
}
|
||
|
||
for _, sf := range srcFields {
|
||
name := c.getFieldName(sf.Field.Name, sf.Tag)
|
||
sField := src.FieldByName(sf.Field.Name)
|
||
|
||
// 是否忽略该字段
|
||
if c.ignore(sField, name, name, sf.Tag) {
|
||
continue
|
||
}
|
||
|
||
if nestedAnonymousField(dst, name) {
|
||
continue
|
||
}
|
||
|
||
dstValue := c.fieldByName(dst, name)
|
||
if dstValue.IsValid() {
|
||
|
||
} else {
|
||
var toMethod reflect.Value
|
||
if dst.CanAddr() {
|
||
toMethod = dst.Addr().MethodByName(name)
|
||
} else {
|
||
toMethod = dst.MethodByName(name)
|
||
}
|
||
|
||
if toMethod.IsValid() && toMethod.Type().NumIn() == 1 && toMethod.Type().In(0) == sField.Type() {
|
||
toMethod.Call([]reflect.Value{sField})
|
||
}
|
||
}
|
||
|
||
if !dstValue.IsValid() {
|
||
continue
|
||
}
|
||
|
||
if c.isCircularRefs(dstValue) {
|
||
continue
|
||
}
|
||
|
||
if ok, err := c.lookupAndCopyWithConverter(dstValue, sField, name); err != nil {
|
||
return err
|
||
} else if ok {
|
||
continue
|
||
}
|
||
|
||
if err := c.deepCopy(dstValue, sField, depth+1); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) copyInterface(dst, src reflect.Value, depth int) error {
|
||
if src.IsNil() {
|
||
return nil
|
||
}
|
||
|
||
srcElem := src.Elem()
|
||
if dst.Kind() == reflect.Interface {
|
||
newValue := reflect.New(srcElem.Type()).Elem()
|
||
if err := c.deepCopy(newValue, srcElem, depth+1); err != nil {
|
||
return err
|
||
}
|
||
|
||
dst.Set(newValue)
|
||
return nil
|
||
}
|
||
|
||
return c.deepCopy(dst, srcElem, depth+1)
|
||
}
|
||
|
||
func (c *copier) copyPointer(dst, src reflect.Value, depth int) error {
|
||
if src.IsNil() {
|
||
return nil
|
||
}
|
||
|
||
srcElem := indirect(src)
|
||
if dst.Kind() == reflect.Pointer {
|
||
if dst.IsNil() {
|
||
dst.Set(reflect.New(dst.Type().Elem()))
|
||
}
|
||
return c.deepCopy(dst.Elem(), srcElem, depth+1)
|
||
}
|
||
|
||
return c.deepCopy(dst, srcElem, depth+1)
|
||
}
|
||
|
||
func (c *copier) copyBasic(dst, src reflect.Value) error {
|
||
if !src.IsValid() {
|
||
return ErrInvalidCopyFrom
|
||
}
|
||
|
||
src = indirect(src)
|
||
dst = ensureSettable(dst)
|
||
if src.Type().AssignableTo(dst.Type()) {
|
||
dst.Set(src)
|
||
return nil
|
||
}
|
||
|
||
if src.Type().ConvertibleTo(dst.Type()) {
|
||
dst.Set(src.Convert(dst.Type()))
|
||
return nil
|
||
}
|
||
|
||
switch dst.Kind() {
|
||
case reflect.String:
|
||
return c.setString(dst, src)
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
return c.setInt(dst, src)
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
return c.setUint(dst, src)
|
||
case reflect.Float32, reflect.Float64:
|
||
return c.setFloat(dst, src)
|
||
case reflect.Bool:
|
||
return c.setBool(dst, src)
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
}
|
||
|
||
func ensureSettable(v reflect.Value) reflect.Value {
|
||
if v.Kind() == reflect.Pointer && v.IsNil() && v.CanSet() {
|
||
v.Set(reflect.New(v.Type().Elem()))
|
||
return v.Elem()
|
||
}
|
||
return v
|
||
}
|
||
|
||
func (c *copier) setString(dst, src reflect.Value) error {
|
||
switch src.Kind() {
|
||
case reflect.String:
|
||
dst.SetString(src.String())
|
||
case reflect.Slice:
|
||
if src.Type().Elem().Kind() == reflect.Uint8 {
|
||
dst.SetString(string(src.Bytes()))
|
||
return nil
|
||
}
|
||
fallthrough
|
||
default:
|
||
dst.SetString(fmt.Sprintf("%v", src.Interface()))
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) setInt(dst, src reflect.Value) error {
|
||
switch src.Kind() {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
dst.SetInt(src.Int())
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
dst.SetInt(int64(src.Uint()))
|
||
case reflect.Float32, reflect.Float64:
|
||
dst.SetInt(int64(src.Float()))
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) setUint(dst, src reflect.Value) error {
|
||
switch src.Kind() {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
dst.SetUint(uint64(src.Int()))
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
dst.SetUint(src.Uint())
|
||
case reflect.Float32, reflect.Float64:
|
||
dst.SetUint(uint64(src.Float()))
|
||
case reflect.String:
|
||
if i, err := strconv.ParseUint(src.String(), 10, 64); err == nil {
|
||
dst.SetUint(i)
|
||
return nil
|
||
}
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) setFloat(dst, src reflect.Value) error {
|
||
switch src.Kind() {
|
||
case reflect.Float32, reflect.Float64:
|
||
dst.SetFloat(src.Float())
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
dst.SetFloat(float64(src.Int()))
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
dst.SetFloat(float64(src.Uint()))
|
||
case reflect.String:
|
||
if f, err := strconv.ParseFloat(src.String(), 64); err == nil {
|
||
dst.SetFloat(f)
|
||
return nil
|
||
}
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) setBool(dst, src reflect.Value) error {
|
||
switch src.Kind() {
|
||
case reflect.Bool:
|
||
dst.SetBool(src.Bool())
|
||
case reflect.String:
|
||
if b, err := strconv.ParseBool(src.String()); err == nil {
|
||
dst.SetBool(b)
|
||
return nil
|
||
}
|
||
// 尝试常见布尔表示
|
||
val := strings.ToLower(src.String())
|
||
if val == "true" || val == "yes" || val == "1" || val == "on" {
|
||
dst.SetBool(true)
|
||
return nil
|
||
}
|
||
if val == "false" || val == "no" || val == "0" || val == "off" {
|
||
dst.SetBool(false)
|
||
return nil
|
||
}
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
dst.SetBool(src.Int() != 0)
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
dst.SetBool(src.Uint() != 0)
|
||
default:
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *copier) ignore(field reflect.Value, srcName, dstName string, tag *tagOption) bool {
|
||
// 忽略空值
|
||
if tag != nil && tag.Contains(tagIgnore) {
|
||
return true
|
||
}
|
||
|
||
// 忽略空值
|
||
if c.opt.ignoreEmpty && field.IsZero() {
|
||
return true
|
||
}
|
||
|
||
// 忽略指定字段
|
||
if c.opt.skipFields != nil {
|
||
if slices.Contains(c.opt.skipFields, srcName) || slices.Contains(c.opt.skipFields, dstName) {
|
||
return true
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
func (c *copier) lookupAndCopyWithConverter(dst, src reflect.Value, fieldName string) (bool, error) {
|
||
if cnv, ok := c.opt.convertByName[fieldName]; ok {
|
||
return convert(dst, src, cnv)
|
||
}
|
||
|
||
if len(c.opt.converters) > 0 {
|
||
pair := converterPair{
|
||
SrcType: src.Type(),
|
||
DstType: dst.Type(),
|
||
}
|
||
|
||
if cnv, ok := c.opt.converters[pair]; ok {
|
||
return convert(dst, src, cnv)
|
||
}
|
||
}
|
||
|
||
return false, nil
|
||
}
|
||
|
||
func (c *copier) copyTime(dst, src reflect.Value) error {
|
||
srcTime, dstTime := getTimeValueExt(src), getTimeValueExt(dst)
|
||
|
||
// 双方都是时间类型,直接赋值(需要处理自定义类型转换)
|
||
if srcTime.IsValid() && dstTime.IsValid() {
|
||
dstTime.Set(srcTime)
|
||
}
|
||
|
||
// 源是时间,目标是其他类型
|
||
if srcTime.IsValid() {
|
||
return c.timeToOther(dst, srcTime.Interface().(time.Time))
|
||
}
|
||
|
||
// 目标是时间,源是其他类型
|
||
if dstTime.IsValid() {
|
||
return c.otherToTime(dst, src)
|
||
}
|
||
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
func (c *copier) timeToOther(dst reflect.Value, src time.Time) error {
|
||
switch dst.Kind() {
|
||
case reflect.String:
|
||
format := c.getTimeFormat(dst.Type())
|
||
dst.SetString(src.Format(format))
|
||
return nil
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
dst.SetInt(src.Unix())
|
||
return nil
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
dst.SetUint(uint64(src.Unix()))
|
||
return nil
|
||
case reflect.Float32, reflect.Float64:
|
||
dst.SetFloat(float64(src.Unix()) + float64(src.Nanosecond())/1e9)
|
||
return nil
|
||
case reflect.Struct:
|
||
if dst.Type() == reflect.TypeOf(time.Time{}) {
|
||
dst.Set(reflect.ValueOf(src))
|
||
return nil
|
||
}
|
||
}
|
||
|
||
return ErrNotSupported(reflect.TypeOf(src), dst.Type())
|
||
}
|
||
|
||
func (c *copier) otherToTime(dst reflect.Value, src reflect.Value) error {
|
||
src = indirect(src)
|
||
switch src.Kind() {
|
||
case reflect.String:
|
||
return c.stringToTime(dst, src.String())
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
dst.Set(reflect.ValueOf(time.Unix(src.Int(), 0)))
|
||
return nil
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
dst.Set(reflect.ValueOf(time.Unix(int64(src.Uint()), 0)))
|
||
return nil
|
||
case reflect.Float32, reflect.Float64:
|
||
sec := src.Float()
|
||
nsec := int64((sec - float64(int64(sec))) * 1e9)
|
||
dst.Set(reflect.ValueOf(time.Unix(int64(sec), nsec)))
|
||
return nil
|
||
case reflect.Struct:
|
||
if src.Type() == reflect.TypeOf(time.Time{}) {
|
||
dst.Set(src)
|
||
return nil
|
||
}
|
||
|
||
case reflect.Map:
|
||
// 支持从map构造时间,如:map[string]int{"year": 2023, "month": 1, "day": 1}
|
||
return c.mapToTime(dst, src)
|
||
}
|
||
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
func (c *copier) getTimeFormat(dstType reflect.Type) string {
|
||
if format, ok := c.opt.timeFormats[dstType]; ok {
|
||
return format
|
||
}
|
||
|
||
if dstType.Kind() == reflect.String {
|
||
if format, ok := c.opt.timeFormats[reflect.TypeOf("")]; ok {
|
||
return format
|
||
}
|
||
}
|
||
|
||
if format, ok := c.opt.timeFormats[reflect.TypeOf(nil)]; ok {
|
||
return format
|
||
}
|
||
|
||
return time.RFC3339
|
||
}
|
||
|
||
func (c *copier) stringToTime(dst reflect.Value, timeStr string) error {
|
||
// 首先尝试数字字符串(时间戳)
|
||
if sec, err := strconv.ParseInt(timeStr, 10, 64); err == nil {
|
||
dst.Set(reflect.ValueOf(time.Unix(sec, 0)))
|
||
return nil
|
||
}
|
||
|
||
// 尝试浮点数时间戳
|
||
if sec, err := strconv.ParseFloat(timeStr, 64); err == nil {
|
||
nsec := int64((sec - float64(int64(sec))) * 1e9)
|
||
dst.Set(reflect.ValueOf(time.Unix(int64(sec), nsec)))
|
||
return nil
|
||
}
|
||
|
||
// 尝试各种时间格式
|
||
formats := c.getTimeFormats(dst.Type())
|
||
for _, format := range formats {
|
||
if t, err := time.Parse(format, timeStr); err == nil {
|
||
c.setTimeValue(dst, t)
|
||
return nil
|
||
}
|
||
}
|
||
|
||
return fmt.Errorf("cannot parse time string: %s", timeStr)
|
||
}
|
||
|
||
func (c *copier) setTimeValue(dst reflect.Value, t time.Time) error {
|
||
if !dst.CanSet() {
|
||
return ErrInvalidCopyDestination
|
||
}
|
||
|
||
// 处理指针类型
|
||
if dst.Kind() == reflect.Pointer {
|
||
if dst.IsNil() {
|
||
if !dst.CanSet() {
|
||
return ErrInvalidCopyDestination
|
||
}
|
||
dst.Set(reflect.New(dst.Type().Elem()))
|
||
}
|
||
dst = dst.Elem()
|
||
}
|
||
|
||
// 目标类型是标准 time.Time
|
||
if dst.Type() == reflect.TypeOf(time.Time{}) {
|
||
dst.Set(reflect.ValueOf(t))
|
||
return nil
|
||
}
|
||
|
||
if setter, ok := dst.Addr().Interface().(interface{ SetTime(time.Time) }); ok {
|
||
setter.SetTime(t)
|
||
return nil
|
||
}
|
||
|
||
return ErrNotSupported(reflect.TypeOf(t), dst.Type())
|
||
}
|
||
|
||
func (c *copier) getTimeFormats(dstType reflect.Type) []string {
|
||
var formats []string
|
||
|
||
// 添加特定类型的格式
|
||
if format, ok := c.opt.timeFormats[dstType]; ok {
|
||
formats = append(formats, format)
|
||
}
|
||
|
||
// 添加字符串类型的格式(如果目标类型是字符串)
|
||
if dstType.Kind() == reflect.String {
|
||
if format, ok := c.opt.timeFormats[reflect.TypeOf("")]; ok {
|
||
formats = append(formats, format)
|
||
}
|
||
}
|
||
|
||
// 添加默认格式
|
||
if format, ok := c.opt.timeFormats[reflect.TypeOf(nil)]; ok {
|
||
formats = append(formats, format)
|
||
}
|
||
|
||
// 添加内置的常见格式
|
||
builtinFormats := []string{
|
||
time.RFC3339,
|
||
time.RFC3339Nano,
|
||
"2006-01-02 15:04:05",
|
||
"2006-01-02",
|
||
"15:04:05",
|
||
time.RFC1123,
|
||
time.RFC822,
|
||
time.ANSIC,
|
||
"2006/01/02",
|
||
"2006/01/02 15:04:05",
|
||
"02 Jan 2006",
|
||
"02 Jan 2006 15:04:05",
|
||
}
|
||
|
||
formats = append(formats, builtinFormats...)
|
||
return formats
|
||
}
|
||
|
||
func (c *copier) mapToTime(dst reflect.Value, src reflect.Value) error {
|
||
if src.Type().Key().Kind() != reflect.String {
|
||
return ErrNotSupported(src.Type(), dst.Type())
|
||
}
|
||
|
||
if src.Len() == 0 {
|
||
return nil
|
||
}
|
||
|
||
// 从map中提取时间组件
|
||
year, month, day, hour, min, sec, nsec := 0, 1, 1, 0, 0, 0, 0
|
||
loc := time.UTC
|
||
|
||
iter := src.MapRange()
|
||
for iter.Next() {
|
||
key := iter.Key().String()
|
||
value := iter.Value()
|
||
|
||
switch key {
|
||
case "year", "Year":
|
||
year = int(getIntValue(value))
|
||
case "month", "Month":
|
||
month = int(getIntValue(value))
|
||
case "day", "Day":
|
||
day = int(getIntValue(value))
|
||
case "hour", "Hour":
|
||
hour = int(getIntValue(value))
|
||
case "minute", "Minute", "min", "Min":
|
||
min = int(getIntValue(value))
|
||
case "second", "Second", "sec", "Sec":
|
||
sec = int(getIntValue(value))
|
||
case "nanosecond", "Nanosecond", "nsec", "Nsec":
|
||
nsec = int(getIntValue(value))
|
||
case "location", "Location", "loc", "Loc":
|
||
if locStr, ok := value.Interface().(string); ok {
|
||
if location, err := time.LoadLocation(locStr); err == nil {
|
||
loc = location
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if year == 0 {
|
||
year = time.Now().Year()
|
||
}
|
||
|
||
t := time.Date(year, time.Month(month), day, hour, min, sec, nsec, loc)
|
||
return c.setTimeValue(dst, t)
|
||
}
|
||
|
||
func getIntValue(v reflect.Value) int64 {
|
||
v = indirect(v)
|
||
switch v.Kind() {
|
||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||
return v.Int()
|
||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||
return int64(v.Uint())
|
||
case reflect.Float32, reflect.Float64:
|
||
return int64(v.Float())
|
||
case reflect.String:
|
||
if i, err := strconv.ParseInt(v.String(), 10, 64); err == nil {
|
||
return i
|
||
}
|
||
}
|
||
|
||
return 0
|
||
}
|
||
|
||
func nestedAnonymousField(dst reflect.Value, fieldName string) bool {
|
||
if f, ok := dst.Type().FieldByName(fieldName); ok {
|
||
if len(f.Index) > 1 {
|
||
destField := dst.Field(f.Index[0])
|
||
|
||
if destField.Kind() != reflect.Pointer {
|
||
return false
|
||
}
|
||
|
||
if !destField.IsNil() {
|
||
return false
|
||
}
|
||
|
||
if destField.CanSet() {
|
||
destField.Set(reflect.New(destField.Type().Elem()))
|
||
} else {
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
func convert(dst, src reflect.Value, fn convertFunc) (bool, error) {
|
||
result, err := fn(src.Interface())
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
if result != nil {
|
||
dst.Set(reflect.ValueOf(result))
|
||
} else {
|
||
dst.Set(reflect.Zero(dst.Type()))
|
||
}
|
||
|
||
return true, nil
|
||
|
||
}
|
||
|
||
func (c *copier) getFieldName(name string, tag *tagOption) string {
|
||
if tag != nil && tag.Contains(tagName) {
|
||
return tag.name
|
||
}
|
||
|
||
return c.opt.NameConvert(name)
|
||
}
|
||
|
||
func (c *copier) getFieldName2(fieldName string, tag *tagOption) (srcFieldName string, dstFieldName string) {
|
||
return fieldName, c.getFieldName(fieldName, tag)
|
||
}
|
||
|
||
func (c *copier) fieldByName(v reflect.Value, name string) reflect.Value {
|
||
if c.opt != nil && c.opt.caseSensitive {
|
||
return v.FieldByName(name)
|
||
}
|
||
return v.FieldByNameFunc(func(s string) bool { return strings.EqualFold(s, name) })
|
||
}
|
||
|
||
func (c *copier) ExceedMaxDepth(depth int) bool {
|
||
return c.opt.maxDepth > 0 && depth >= c.opt.maxDepth
|
||
}
|
||
|
||
func (c *copier) isCircularRefs(v reflect.Value) bool {
|
||
if !c.opt.detectCircularRefs || !v.IsValid() || !v.CanAddr() {
|
||
return false
|
||
}
|
||
|
||
ptr := c.getValuePointer(v)
|
||
if _, exists := c.visited[ptr]; exists {
|
||
return true
|
||
}
|
||
|
||
c.visited[ptr] = struct{}{}
|
||
return false
|
||
}
|
||
|
||
func (c *copier) getValuePointer(v reflect.Value) uintptr {
|
||
switch v.Kind() {
|
||
case reflect.Pointer, reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
|
||
return v.Pointer()
|
||
case reflect.Struct:
|
||
if v.CanAddr() {
|
||
return v.Addr().Pointer()
|
||
}
|
||
}
|
||
|
||
return 0
|
||
}
|
||
|
||
func isTimeType(t reflect.Type) bool {
|
||
if t == nil {
|
||
return false
|
||
}
|
||
|
||
// 处理指针类型
|
||
for t.Kind() == reflect.Pointer {
|
||
t = t.Elem()
|
||
}
|
||
|
||
// 直接比较标准 time.Time 类型
|
||
if t == reflect.TypeOf(time.Time{}) {
|
||
return true
|
||
}
|
||
|
||
if _, ok := t.MethodByName("Time"); ok {
|
||
return true
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
func getTimeValue(v reflect.Value) reflect.Value {
|
||
if !v.IsValid() {
|
||
return reflect.Value{}
|
||
}
|
||
|
||
// 标准 time.Time 类型
|
||
if v.Type() == reflect.TypeOf(time.Time{}) {
|
||
return v
|
||
}
|
||
|
||
// 检查是否为时间类型
|
||
if isTimeType(v.Type()) {
|
||
return v
|
||
}
|
||
|
||
// 指针类型处理
|
||
if v.Kind() == reflect.Pointer && !v.IsNil() {
|
||
elem := v.Elem()
|
||
if isTimeType(elem.Type()) {
|
||
return elem
|
||
}
|
||
}
|
||
|
||
return reflect.Value{}
|
||
}
|
||
|
||
func getTimeValueExt(v reflect.Value) reflect.Value {
|
||
standardTime := getTimeValue(v)
|
||
if standardTime.IsValid() {
|
||
return standardTime
|
||
}
|
||
|
||
// 如果无法直接识别,尝试转换
|
||
if v.IsValid() && v.CanInterface() {
|
||
// 尝试通过接口转换
|
||
if converter, ok := v.Interface().(interface{ ToTime() time.Time }); ok {
|
||
return reflect.ValueOf(converter.ToTime())
|
||
}
|
||
|
||
// 尝试直接转换
|
||
if v.Type().ConvertibleTo(reflect.TypeOf(time.Time{})) {
|
||
return v.Convert(reflect.TypeOf(time.Time{}))
|
||
}
|
||
}
|
||
|
||
return reflect.Value{}
|
||
}
|
||
|
||
func indirect(v reflect.Value) reflect.Value {
|
||
for v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface {
|
||
if v.IsNil() {
|
||
break
|
||
}
|
||
v = v.Elem()
|
||
}
|
||
|
||
return v
|
||
}
|
||
|
||
func indirectType(reflectType reflect.Type) (_ reflect.Type, isPtr bool) {
|
||
for reflectType.Kind() == reflect.Pointer || reflectType.Kind() == reflect.Slice {
|
||
reflectType = reflectType.Elem()
|
||
isPtr = true
|
||
}
|
||
|
||
return reflectType, isPtr
|
||
}
|