From 08bd59d64734fb050aed6a965789e92e7bc58b14 Mon Sep 17 00:00:00 2001 From: Andraz Bajt Date: Wed, 30 Apr 2014 11:08:56 +0200 Subject: [PATCH 1/2] support for pointer fields --- mergo.go | 14 +++++++++++--- mergo_test.go | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/mergo.go b/mergo.go index 50d7666..5190e67 100644 --- a/mergo.go +++ b/mergo.go @@ -72,7 +72,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int) er switch dst.Kind() { case reflect.Struct: for i, n := 0, dst.NumField(); i < n; i++ { - if err := deepMerge(dst.Field(i), src.Field(i), visited, depth + 1); err != nil { + if err := deepMerge(dst.Field(i), src.Field(i), visited, depth+1); err != nil { return err } } @@ -87,7 +87,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int) er case reflect.Struct: fallthrough case reflect.Map: - if err := deepMerge(dstElement, srcElement, visited, depth + 1); err != nil { + if err := deepMerge(dstElement, srcElement, visited, depth+1); err != nil { return err } } @@ -95,8 +95,16 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int) er dst.SetMapIndex(key, srcElement) } } + case reflect.Ptr: + fallthrough case reflect.Interface: - if err := deepMerge(dst.Elem(), src.Elem(), visited, depth + 1); err != nil { + if src.IsNil() { + break + } else if dst.IsNil() { + if dst.CanSet() && isEmptyValue(dst) { + dst.Set(src) + } + } else if err := deepMerge(dst.Elem(), src.Elem(), visited, depth+1); err != nil { return err } default: diff --git a/mergo_test.go b/mergo_test.go index 95f112e..8764824 100644 --- a/mergo_test.go +++ b/mergo_test.go @@ -6,10 +6,10 @@ package mergo import ( + "io/ioutil" + "launchpad.net/goyaml" "reflect" "testing" - "launchpad.net/goyaml" - "io/ioutil" ) type simpleTest struct { @@ -22,6 +22,10 @@ type complexTest struct { Id string } +type pointerTest struct { + C *simpleTest +} + func TestNil(t *testing.T) { if err := Merge(nil, nil); err != NilArgumentsErr { t.Fail() @@ -68,6 +72,30 @@ func TestComplexStruct(t *testing.T) { } } +func TestPointerStruct(t *testing.T) { + s1 := simpleTest{} + s2 := simpleTest{19} + a := pointerTest{&s1} + b := pointerTest{&s2} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if a.C.Value != b.C.Value { + //t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) + } +} + +func TestPointerStructNil(t *testing.T) { + a := pointerTest{nil} + b := pointerTest{&simpleTest{19}} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if a.C.Value != b.C.Value { + t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) + } +} + func TestMaps(t *testing.T) { m := map[string]simpleTest{ "a": simpleTest{}, From 47edc315805380349364ae0bf4f69b97958f1e20 Mon Sep 17 00:00:00 2001 From: Andraz Bajt Date: Wed, 30 Apr 2014 11:21:59 +0200 Subject: [PATCH 2/2] changed merge semantics: if an atomic value exists in the dst but it also exists in the src it will be overwritten. --- mergo.go | 2 +- mergo_test.go | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/mergo.go b/mergo.go index 5190e67..6abea8c 100644 --- a/mergo.go +++ b/mergo.go @@ -108,7 +108,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int) er return err } default: - if dst.CanSet() && isEmptyValue(dst) { + if dst.CanSet() && !isEmptyValue(src) { dst.Set(src) } } diff --git a/mergo_test.go b/mergo_test.go index 8764824..fd2f07a 100644 --- a/mergo_test.go +++ b/mergo_test.go @@ -26,6 +26,10 @@ type pointerTest struct { C *simpleTest } +type sliceTest struct { + S []int +} + func TestNil(t *testing.T) { if err := Merge(nil, nil); err != NilArgumentsErr { t.Fail() @@ -67,8 +71,8 @@ func TestComplexStruct(t *testing.T) { if a.sz == 1 { t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz) } - if a.Id == b.Id { - t.Fatalf("a's field Id not preserved from merge: a.Id(%s) == b.Id(%s)", a.Id, b.Id) + if a.Id != b.Id { + t.Fatalf("a's field Id not merged properly: a.Id(%s) != b.Id(%s)", a.Id, b.Id) } } @@ -96,6 +100,32 @@ func TestPointerStructNil(t *testing.T) { } } +func TestSliceStruct(t *testing.T) { + a := sliceTest{} + b := sliceTest{[]int{1, 2, 3}} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if len(b.S) != 3 { + t.FailNow() + } + if len(a.S) != len(b.S) { + t.Fatalf("b not merged in a properly %d != %d", len(a.S), len(b.S)) + } + + a = sliceTest{[]int{1}} + b = sliceTest{[]int{1, 2, 3}} + if err := Merge(&a, b); err != nil { + t.FailNow() + } + if len(b.S) != 3 { + t.FailNow() + } + if len(a.S) != len(b.S) { + t.Fatalf("b not merged in a properly %d != %d", len(a.S), len(b.S)) + } +} + func TestMaps(t *testing.T) { m := map[string]simpleTest{ "a": simpleTest{},