Files
copier/copier.go
T
charlie a6990aa4af 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.
2026-04-24 01:11:22 +08:00

105 lines
2.1 KiB
Go

package copier
import (
"reflect"
"sync"
)
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() {
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
}
// 可以直接赋值时直接赋值并返回
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)
}
}