refactor: split copier.go into smaller files
Split monolithic copier.go (1077 lines) into focused modules: - copier.go: Core framework (95 lines) - copy_value.go: Copy functions and type handling (858 lines) - reflect.go: Reflection utilities (100 lines) - converter.go: Type converter (38 lines) - options.go: Configuration (renamed from optiongs.go) Bug fixes: - Fixed string to *string copying (empty strings now work) - Fixed *time.Time to *time.Time copying - Fixed nil slice handling - Fixed copyStructToSlice pointer element handling Tests: Fixed critical data copy issues. Remaining method-mapping features require additional implementation.
This commit is contained in:
+890
@@ -0,0 +1,890 @@
|
||||
package copier
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 追加新元素(结构体)
|
||||
elem := newSlice.Index(dstLen)
|
||||
|
||||
// 如果 slice 元素是指针类型,需要先创建实例
|
||||
if elem.Kind() == reflect.Pointer {
|
||||
if elem.IsNil() {
|
||||
elem.Set(reflect.New(elem.Type().Elem()))
|
||||
}
|
||||
elem = elem.Elem()
|
||||
}
|
||||
|
||||
if err := c.deepCopy(elem, 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()
|
||||
dstLen := dst.Len()
|
||||
|
||||
// 创建新 slice,保留原有元素 + 追加 src 元素
|
||||
newLen := dstLen + srcLen
|
||||
newSlice := reflect.MakeSlice(dst.Type(), newLen, newLen)
|
||||
|
||||
// 复制原有元素
|
||||
for i := range dstLen {
|
||||
if err := c.deepCopy(newSlice.Index(i), dst.Index(i), depth+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 追加 src 元素
|
||||
for i := range srcLen {
|
||||
if err := c.deepCopy(newSlice.Index(dstLen+i), src.Index(i), depth+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dst.Set(newSlice)
|
||||
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:
|
||||
// 只有当 src 不是 struct 时才调用 copyStructToSlice
|
||||
// 否则应该将 src struct 追加到 dst slice
|
||||
if src.Kind() == reflect.Struct {
|
||||
return c.copyStructToSlice(dst, src, depth)
|
||||
}
|
||||
return ErrNotSupported(src.Type(), dst.Type())
|
||||
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() {
|
||||
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})
|
||||
}
|
||||
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) copyTime(dst, src reflect.Value) error {
|
||||
srcTime, dstTime := getTimeValueExt(src), getTimeValueExt(dst)
|
||||
|
||||
// 双方都是时间类型,直接赋值(需要处理自定义类型转换)
|
||||
if srcTime.IsValid() && dstTime.IsValid() {
|
||||
dstTime.Set(srcTime)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 源是时间,目标是其他类型(包括 nil 指针)
|
||||
if srcTime.IsValid() {
|
||||
srcTimeValue := srcTime.Interface()
|
||||
if t, ok := srcTimeValue.(time.Time); ok {
|
||||
return c.timeToOther(dst, t)
|
||||
}
|
||||
if t, ok := srcTimeValue.(*time.Time); ok && t != nil {
|
||||
return c.timeToOther(dst, *t)
|
||||
}
|
||||
}
|
||||
|
||||
// 目标是时间(可能是 nil 指针),源是其他类型
|
||||
if dstTime.IsValid() || isTimeType(dst.Type()) {
|
||||
// 如果 dst 是 nil 指针,先创建实例
|
||||
if dst.Kind() == reflect.Pointer && dst.IsNil() && dst.CanSet() {
|
||||
dst.Set(reflect.New(dst.Type().Elem()))
|
||||
dstTime = getTimeValueExt(dst)
|
||||
}
|
||||
if dstTime.IsValid() {
|
||||
return c.otherToTime(dstTime, 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 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{}
|
||||
}
|
||||
Reference in New Issue
Block a user