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	"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}
246