From a6990aa4afbe259fe0d4e95dea1d191157aba4d2 Mon Sep 17 00:00:00 2001 From: charlie <3140647@qq.com> Date: Fri, 24 Apr 2026 01:11:22 +0800 Subject: [PATCH] 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. --- converter.go | 38 ++ copier.go | 995 +------------------------------------- copier_slice_test.go | 6 +- copy_value.go | 890 ++++++++++++++++++++++++++++++++++ optiongs.go => options.go | 0 reflect.go | 100 ++++ 6 files changed, 1043 insertions(+), 986 deletions(-) create mode 100644 converter.go create mode 100644 copy_value.go rename optiongs.go => options.go (100%) create mode 100644 reflect.go diff --git a/converter.go b/converter.go new file mode 100644 index 0000000..adf8680 --- /dev/null +++ b/converter.go @@ -0,0 +1,38 @@ +package copier + +import "reflect" + +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 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 + +} diff --git a/copier.go b/copier.go index d75af43..08935f0 100644 --- a/copier.go +++ b/copier.go @@ -1,13 +1,8 @@ package copier import ( - "fmt" "reflect" - "slices" - "strconv" - "strings" "sync" - "time" ) type copier struct { @@ -62,11 +57,20 @@ func (c *copier) deepCopy(dst, src reflect.Value, depth int) error { return ErrMaxDepthExceeded } - // 复制源为空值,直接返回 - if !src.IsValid() || src.IsZero() { + // 复制源无效,直接返回 + if !src.IsValid() { return nil } + // 对于 slice 和 map,如果 src 是 nil,不复制(保持 dst 不变) + // 对于其他类型(如 string),即使 IsZero 也要复制 + switch src.Kind() { + case reflect.Slice, reflect.Map: + if src.IsNil() { + return nil + } + } + if c.isCircularRefs(src) { return ErrCircularReference } @@ -98,980 +102,3 @@ func (c *copier) deepCopy(dst, src reflect.Value, depth int) error { 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 -} diff --git a/copier_slice_test.go b/copier_slice_test.go index 8221c30..d44b0d0 100644 --- a/copier_slice_test.go +++ b/copier_slice_test.go @@ -138,8 +138,10 @@ func TestCopyFromStructToSlice(t *testing.T) { t.Errorf("Copy to unaddressable value should get error") } - if Copy(&employees, &user); len(employees) != 1 { - t.Errorf("Should only have one elem when copy struct to slice") + if err := Copy(&employees, &user); err != nil { + t.Errorf("Copy failed: %v", err) + } else if len(employees) != 1 { + t.Errorf("Should only have one elem when copy struct to slice, got %d", len(employees)) } else { checkEmployee(employees[0], user, t, "Copy From Struct To Slice Ptr") } diff --git a/copy_value.go b/copy_value.go new file mode 100644 index 0000000..b53025e --- /dev/null +++ b/copy_value.go @@ -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{} +} diff --git a/optiongs.go b/options.go similarity index 100% rename from optiongs.go rename to options.go diff --git a/reflect.go b/reflect.go new file mode 100644 index 0000000..a4c7c16 --- /dev/null +++ b/reflect.go @@ -0,0 +1,100 @@ +package copier + +import ( + "reflect" + "strings" +) + +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 (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 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 +}