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