blob: f8de6c54305aa3723fd32301c4ebaf32856cd5be [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001// Copyright 2013 Dario Castañé. All rights reserved.
2// Copyright 2009 The Go Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6// Based on src/pkg/reflect/deepequal.go from official
7// golang's stdlib.
8
9package mergo
10
11import (
12 "fmt"
13 "reflect"
14)
15
16func hasExportedField(dst reflect.Value) (exported bool) {
17 for i, n := 0, dst.NumField(); i < n; i++ {
18 field := dst.Type().Field(i)
19 if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
20 exported = exported || hasExportedField(dst.Field(i))
21 } else {
22 exported = exported || len(field.PkgPath) == 0
23 }
24 }
25 return
26}
27
28type Config struct {
29 Overwrite bool
30 AppendSlice bool
31 Transformers Transformers
32 overwriteWithEmptyValue bool
33}
34
35type Transformers interface {
36 Transformer(reflect.Type) func(dst, src reflect.Value) error
37}
38
39// Traverses recursively both values, assigning src's fields values to dst.
40// The map argument tracks comparisons that have already been seen, which allows
41// short circuiting on recursive types.
42func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
43 overwrite := config.Overwrite
44 overwriteWithEmptySrc := config.overwriteWithEmptyValue
45 config.overwriteWithEmptyValue = false
46
47 if !src.IsValid() {
48 return
49 }
50 if dst.CanAddr() {
51 addr := dst.UnsafeAddr()
52 h := 17 * addr
53 seen := visited[h]
54 typ := dst.Type()
55 for p := seen; p != nil; p = p.next {
56 if p.ptr == addr && p.typ == typ {
57 return nil
58 }
59 }
60 // Remember, remember...
61 visited[h] = &visit{addr, typ, seen}
62 }
63
64 if config.Transformers != nil && !isEmptyValue(dst) {
65 if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
66 err = fn(dst, src)
67 return
68 }
69 }
70
71 switch dst.Kind() {
72 case reflect.Struct:
73 if hasExportedField(dst) {
74 for i, n := 0, dst.NumField(); i < n; i++ {
75 if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
76 return
77 }
78 }
79 } else {
80 if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
81 dst.Set(src)
82 }
83 }
84 case reflect.Map:
85 if dst.IsNil() && !src.IsNil() {
86 dst.Set(reflect.MakeMap(dst.Type()))
87 }
88 for _, key := range src.MapKeys() {
89 srcElement := src.MapIndex(key)
90 if !srcElement.IsValid() {
91 continue
92 }
93 dstElement := dst.MapIndex(key)
94 switch srcElement.Kind() {
95 case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
96 if srcElement.IsNil() {
97 continue
98 }
99 fallthrough
100 default:
101 if !srcElement.CanInterface() {
102 continue
103 }
104 switch reflect.TypeOf(srcElement.Interface()).Kind() {
105 case reflect.Struct:
106 fallthrough
107 case reflect.Ptr:
108 fallthrough
109 case reflect.Map:
110 srcMapElm := srcElement
111 dstMapElm := dstElement
112 if srcMapElm.CanInterface() {
113 srcMapElm = reflect.ValueOf(srcMapElm.Interface())
114 if dstMapElm.IsValid() {
115 dstMapElm = reflect.ValueOf(dstMapElm.Interface())
116 }
117 }
118 if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
119 return
120 }
121 case reflect.Slice:
122 srcSlice := reflect.ValueOf(srcElement.Interface())
123
124 var dstSlice reflect.Value
125 if !dstElement.IsValid() || dstElement.IsNil() {
126 dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
127 } else {
128 dstSlice = reflect.ValueOf(dstElement.Interface())
129 }
130
131 if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
132 dstSlice = srcSlice
133 } else if config.AppendSlice {
134 if srcSlice.Type() != dstSlice.Type() {
135 return fmt.Errorf("cannot append two slice with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
136 }
137 dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
138 }
139 dst.SetMapIndex(key, dstSlice)
140 }
141 }
142 if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
143 continue
144 }
145
146 if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) {
147 if dst.IsNil() {
148 dst.Set(reflect.MakeMap(dst.Type()))
149 }
150 dst.SetMapIndex(key, srcElement)
151 }
152 }
153 case reflect.Slice:
154 if !dst.CanSet() {
155 break
156 }
157 if (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
158 dst.Set(src)
159 } else if config.AppendSlice {
160 if src.Type() != dst.Type() {
161 return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
162 }
163 dst.Set(reflect.AppendSlice(dst, src))
164 }
165 case reflect.Ptr:
166 fallthrough
167 case reflect.Interface:
168 if src.IsNil() {
169 break
170 }
171 if src.Kind() != reflect.Interface {
172 if dst.IsNil() || overwrite {
173 if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
174 dst.Set(src)
175 }
176 } else if src.Kind() == reflect.Ptr {
177 if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
178 return
179 }
180 } else if dst.Elem().Type() == src.Type() {
181 if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
182 return
183 }
184 } else {
185 return ErrDifferentArgumentsTypes
186 }
187 break
188 }
189 if dst.IsNil() || overwrite {
190 if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
191 dst.Set(src)
192 }
193 } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
194 return
195 }
196 default:
197 if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
198 dst.Set(src)
199 }
200 }
201 return
202}
203
204// Merge will fill any empty for value type attributes on the dst struct using corresponding
205// src attributes if they themselves are not empty. dst and src must be valid same-type structs
206// and dst must be a pointer to struct.
207// It won't merge unexported (private) fields and will do recursively any exported field.
208func Merge(dst, src interface{}, opts ...func(*Config)) error {
209 return merge(dst, src, opts...)
210}
211
212// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
213// non-empty src attribute values.
214// Deprecated: use Merge(…) with WithOverride
215func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
216 return merge(dst, src, append(opts, WithOverride)...)
217}
218
219// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
220func WithTransformers(transformers Transformers) func(*Config) {
221 return func(config *Config) {
222 config.Transformers = transformers
223 }
224}
225
226// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
227func WithOverride(config *Config) {
228 config.Overwrite = true
229}
230
231// WithAppendSlice will make merge append slices instead of overwriting it
232func WithAppendSlice(config *Config) {
233 config.AppendSlice = true
234}
235
236func merge(dst, src interface{}, opts ...func(*Config)) error {
237 var (
238 vDst, vSrc reflect.Value
239 err error
240 )
241
242 config := &Config{}
243
244 for _, opt := range opts {
245 opt(config)
246 }
247
248 if vDst, vSrc, err = resolveValues(dst, src); err != nil {
249 return err
250 }
251 if vDst.Type() != vSrc.Type() {
252 return ErrDifferentArgumentsTypes
253 }
254 return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
255}