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