Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / github.com / imdario / mergo / merge.go
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
9 package mergo
10
11 import (
12         "fmt"
13         "reflect"
14 )
15
16 func 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
28 type Config struct {
29         Overwrite               bool
30         AppendSlice             bool
31         Transformers            Transformers
32         overwriteWithEmptyValue bool
33 }
34
35 type 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.
42 func 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.
208 func 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
215 func 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.
220 func 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.
227 func WithOverride(config *Config) {
228         config.Overwrite = true
229 }
230
231 // WithAppendSlice will make merge append slices instead of overwriting it
232 func WithAppendSlice(config *Config) {
233         config.AppendSlice = true
234 }
235
236 func 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 }