Files
copier/copier.go
2025-09-30 18:41:21 +08:00

1078 lines
24 KiB
Go
Raw Permalink Blame History

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