aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/imdario/mergo/merge.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/imdario/mergo/merge.go')
-rw-r--r--vendor/github.com/imdario/mergo/merge.go90
1 files changed, 71 insertions, 19 deletions
diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/github.com/imdario/mergo/merge.go
index 052b9fe78..04629a3dc 100644
--- a/vendor/github.com/imdario/mergo/merge.go
+++ b/vendor/github.com/imdario/mergo/merge.go
@@ -8,14 +8,12 @@
package mergo
-import (
- "reflect"
-)
+import "reflect"
func hasExportedField(dst reflect.Value) (exported bool) {
for i, n := 0, dst.NumField(); i < n; i++ {
field := dst.Type().Field(i)
- if field.Anonymous {
+ if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
exported = exported || hasExportedField(dst.Field(i))
} else {
exported = exported || len(field.PkgPath) == 0
@@ -24,10 +22,21 @@ func hasExportedField(dst reflect.Value) (exported bool) {
return
}
+type config struct {
+ overwrite bool
+ transformers transformers
+}
+
+type transformers interface {
+ Transformer(reflect.Type) func(dst, src reflect.Value) error
+}
+
// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
-func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
+func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *config) (err error) {
+ overwrite := config.overwrite
+
if !src.IsValid() {
return
}
@@ -44,11 +53,19 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
// Remember, remember...
visited[h] = &visit{addr, typ, seen}
}
+
+ if config.transformers != nil {
+ if fn := config.transformers.Transformer(dst.Type()); fn != nil {
+ err = fn(dst, src)
+ return
+ }
+ }
+
switch dst.Kind() {
case reflect.Struct:
if hasExportedField(dst) {
for i, n := 0, dst.NumField(); i < n; i++ {
- if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
+ if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
return
}
}
@@ -84,9 +101,21 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
case reflect.Ptr:
fallthrough
case reflect.Map:
- if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
+ if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
+ case reflect.Slice:
+ srcSlice := reflect.ValueOf(srcElement.Interface())
+
+ var dstSlice reflect.Value
+ if !dstElement.IsValid() || dstElement.IsNil() {
+ dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
+ } else {
+ dstSlice = reflect.ValueOf(dstElement.Interface())
+ }
+
+ dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
+ dst.SetMapIndex(key, dstSlice)
}
}
if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
@@ -100,20 +129,25 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
dst.SetMapIndex(key, srcElement)
}
}
+ case reflect.Slice:
+ dst.Set(reflect.AppendSlice(dst, src))
case reflect.Ptr:
fallthrough
case reflect.Interface:
+ if src.IsNil() {
+ break
+ }
if src.Kind() != reflect.Interface {
if dst.IsNil() || overwrite {
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
dst.Set(src)
}
} else if src.Kind() == reflect.Ptr {
- if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
+ if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
} else if dst.Elem().Type() == src.Type() {
- if err = deepMerge(dst.Elem(), src, visited, depth+1, overwrite); err != nil {
+ if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
return
}
} else {
@@ -121,13 +155,11 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
}
break
}
- if src.IsNil() {
- break
- } else if dst.IsNil() || overwrite {
+ if dst.IsNil() || overwrite {
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
dst.Set(src)
}
- } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
+ } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
default:
@@ -142,26 +174,46 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
// and dst must be a pointer to struct.
// It won't merge unexported (private) fields and will do recursively any exported field.
-func Merge(dst, src interface{}) error {
- return merge(dst, src, false)
+func Merge(dst, src interface{}, opts ...func(*config)) error {
+ return merge(dst, src, opts...)
}
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
// non-empty src attribute values.
-func MergeWithOverwrite(dst, src interface{}) error {
- return merge(dst, src, true)
+// Deprecated: use Merge(…) with WithOverride
+func MergeWithOverwrite(dst, src interface{}, opts ...func(*config)) error {
+ return merge(dst, src, append(opts, WithOverride)...)
+}
+
+// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
+func WithTransformers(transformers transformers) func(*config) {
+ return func(config *config) {
+ config.transformers = transformers
+ }
}
-func merge(dst, src interface{}, overwrite bool) error {
+// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
+func WithOverride(config *config) {
+ config.overwrite = true
+}
+
+func merge(dst, src interface{}, opts ...func(*config)) error {
var (
vDst, vSrc reflect.Value
err error
)
+
+ config := &config{}
+
+ for _, opt := range opts {
+ opt(config)
+ }
+
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
return err
}
if vDst.Type() != vSrc.Type() {
return ErrDifferentArgumentsTypes
}
- return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
+ return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}