From 424e4b228efa3a2a4442750167a08895ac58fbd9 Mon Sep 17 00:00:00 2001 From: "Christian G. Warden" Date: Fri, 26 Feb 2021 07:16:47 -0600 Subject: [PATCH] Add WithNoOverrideEmptyStructValues Add option to not overwrite empty fields in destination structs if the structs are not empty. --- emptyStructValues_test.go | 49 +++++++++++++++++++++++++++++++++++++++ merge.go | 27 ++++++++++++++------- 2 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 emptyStructValues_test.go diff --git a/emptyStructValues_test.go b/emptyStructValues_test.go new file mode 100644 index 0000000..d123103 --- /dev/null +++ b/emptyStructValues_test.go @@ -0,0 +1,49 @@ +package mergo_test + +import ( + "testing" + + "github.com/imdario/mergo" +) + +type parent struct { + S string + Child *child +} + +type child struct { + S string +} + +func TestDoNotOverwriteEmptyValueWithinStruct(t *testing.T) { + src := parent{ + Child: &child{S: "src"}, + S: "src", + } + dest := parent{ + Child: &child{S: ""}, + S: "dest", + } + if err := mergo.Merge(&dest, src, mergo.WithNoOverrideEmptyStructValues); err != nil { + t.Error(err) + } + if dest.Child.S != "" { + t.Errorf("dest.Child.S overwritten") + } +} + +func TestOverwriteEmptyStruct(t *testing.T) { + src := parent{ + Child: &child{S: "src"}, + S: "src", + } + dest := parent{ + S: "dest", + } + if err := mergo.Merge(&dest, src, mergo.WithNoOverrideEmptyStructValues); err != nil { + t.Error(err) + } + if dest.Child.S != "src" { + t.Errorf("dest.Child.S not overwritten") + } +} diff --git a/merge.go b/merge.go index afa84a1..e5ebb90 100644 --- a/merge.go +++ b/merge.go @@ -38,14 +38,15 @@ func isExportedComponent(field *reflect.StructField) bool { } type Config struct { - Overwrite bool - AppendSlice bool - TypeCheck bool - Transformers Transformers - overwriteWithEmptyValue bool - overwriteSliceWithEmptyValue bool - sliceDeepCopy bool - debug bool + Overwrite bool + AppendSlice bool + TypeCheck bool + Transformers Transformers + overwriteWithEmptyValue bool + overwriteSliceWithEmptyValue bool + doNotOverwriteEmptyStructValuesInDst bool + sliceDeepCopy bool + debug bool } type Transformers interface { @@ -60,6 +61,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co typeCheck := config.TypeCheck overwriteWithEmptySrc := config.overwriteWithEmptyValue overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue + doNotOverwriteEmptyStructValuesInDst := config.doNotOverwriteEmptyStructValuesInDst sliceDeepCopy := config.sliceDeepCopy if !src.IsValid() { @@ -86,9 +88,11 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co } } + overwriteStructValues := depth == 0 || !doNotOverwriteEmptyStructValuesInDst + switch dst.Kind() { case reflect.Struct: - if hasMergeableFields(dst) { + if overwriteStructValues && hasMergeableFields(dst) { for i, n := 0, dst.NumField(); i < n; i++ { if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { return @@ -321,6 +325,11 @@ func WithOverrideEmptySlice(config *Config) { config.overwriteSliceWithEmptyValue = true } +// WithNoOverrideEmptyStructValues will make merge not overwrite the empty values within non-empty structs in dst +func WithNoOverrideEmptyStructValues(config *Config) { + config.doNotOverwriteEmptyStructValuesInDst = true +} + // WithAppendSlice will make merge append slices instead of overwriting it. func WithAppendSlice(config *Config) { config.AppendSlice = true