-
Notifications
You must be signed in to change notification settings - Fork 0
/
deepcopy.go
116 lines (98 loc) · 3.17 KB
/
deepcopy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package deepcopy
import (
"fmt"
"reflect"
"strings"
"sync"
)
const (
DefaultTagName = "copy"
)
var (
// defaultTagName default tag name for the program to parse input struct tags
// to build copier configuration.
defaultTagName = DefaultTagName
)
// Context copier context
type Context struct {
// CopyBetweenPtrAndValue allow or not copying between pointers and values (default is `true`)
CopyBetweenPtrAndValue bool
// CopyBetweenStructFieldAndMethod allow or not copying between struct fields and methods (default is `true`)
CopyBetweenStructFieldAndMethod bool
// IgnoreNonCopyableTypes ignore non-copyable types (default is `false`)
IgnoreNonCopyableTypes bool
// UseGlobalCache if false not use global cache (default is `true`)
UseGlobalCache bool
// copierCacheMap cache to speed up parsing types
copierCacheMap map[cacheKey]copier
mu *sync.RWMutex
flags uint8
}
// Option configuration option function provided as extra arguments of copying function
type Option func(ctx *Context)
// CopyBetweenPtrAndValue config function for setting flag `CopyBetweenPtrAndValue`
func CopyBetweenPtrAndValue(flag bool) Option {
return func(ctx *Context) {
ctx.CopyBetweenPtrAndValue = flag
}
}
// CopyBetweenStructFieldAndMethod config function for setting flag `CopyBetweenStructFieldAndMethod`
func CopyBetweenStructFieldAndMethod(flag bool) Option {
return func(ctx *Context) {
ctx.CopyBetweenStructFieldAndMethod = flag
}
}
// IgnoreNonCopyableTypes config function for setting flag `IgnoreNonCopyableTypes`
func IgnoreNonCopyableTypes(flag bool) Option {
return func(ctx *Context) {
ctx.IgnoreNonCopyableTypes = flag
}
}
// UseGlobalCache config function for setting flag `UseGlobalCache`
func UseGlobalCache(flag bool) Option {
return func(ctx *Context) {
ctx.UseGlobalCache = flag
}
}
// Copy performs deep copy from `src` to `dst`.
//
// `dst` must be a pointer to the output var, `src` can be either value or pointer.
// In case you want to copy unexported struct fields within `src`, `src` must be a pointer.
func Copy(dst, src any, options ...Option) (err error) {
if src == nil || dst == nil {
return fmt.Errorf("%w: source and destination must be non-nil", ErrValueInvalid)
}
dstVal, srcVal := reflect.ValueOf(dst), reflect.ValueOf(src)
dstType, srcType := dstVal.Type(), srcVal.Type()
if dstType.Kind() != reflect.Pointer {
return fmt.Errorf("%w: destination must be pointer", ErrTypeInvalid)
}
dstVal, dstType = dstVal.Elem(), dstType.Elem()
if !dstVal.IsValid() {
return fmt.Errorf("%w: destination must be non-nil", ErrValueInvalid)
}
ctx := defaultContext()
for _, opt := range options {
opt(ctx)
}
ctx.prepare()
cp, err := buildCopier(ctx, dstType, srcType)
if err != nil {
return err
}
return cp.Copy(dstVal, srcVal)
}
// ClearCache clears global cache of previously used copiers
func ClearCache() {
mu.Lock()
copierCacheMap = map[cacheKey]copier{}
mu.Unlock()
}
// SetDefaultTagName overwrites the default tag name.
// This function should only be called once at program startup.
func SetDefaultTagName(tag string) {
tagName := strings.TrimSpace(tag)
if tagName != "" && tagName == tag {
defaultTagName = tagName
}
}