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:
2026-04-24 01:11:22 +08:00
parent 362dc000b1
commit a6990aa4af
6 changed files with 1043 additions and 986 deletions
+38
View File
@@ -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
}
+11 -984
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -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")
}
+890
View File
@@ -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{}
}
View File
+100
View File
@@ -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
}