Commit ef0a383e authored by Tomasz Maczukin's avatar Tomasz Maczukin

Update github.com/imdario/mergo version

parent 9c59020d
......@@ -411,11 +411,12 @@
revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
[[projects]]
digest = "1:ff6e83dd3b5a66d41c58a3d5e144bd06fcd4c376fd1a84379ad8c9a7dd308643"
digest = "1:158a976af1c463daebfa72efdfb66ccd78edfa475ed6d66937744bca05ee6c9c"
name = "github.com/imdario/mergo"
packages = ["."]
pruneopts = "N"
revision = "50d4dbd4eb0e84778abe37cefef140271d96fade"
revision = "7c29201646fa3de8506f701213473dd407f19646"
version = "v0.3.7"
[[projects]]
digest = "1:485d36dae750af66fac78a968360bb1eb5a635d9d1e3a3b92116556cc2d45b6a"
......@@ -1066,6 +1067,7 @@
"github.com/gorilla/mux",
"github.com/gorilla/websocket",
"github.com/hashicorp/go-version",
"github.com/imdario/mergo",
"github.com/jpillora/backoff",
"github.com/kardianos/osext",
"github.com/kr/pty",
......
......@@ -166,6 +166,10 @@ ignored = ["test", "appengine"]
name = "github.com/gofrs/flock"
branch = "master"
[[constraint]]
name = "github.com/imdario/mergo"
version = "v0.3.7"
##
## Refrain innovations ;)
##
......@@ -230,10 +234,6 @@ ignored = ["test", "appengine"]
name = "github.com/gorilla/mux"
revision = "599cba5e7b6137d46ddf58fb1765f5d928e69604"
[[override]]
name = "github.com/imdario/mergo"
revision = "50d4dbd4eb0e84778abe37cefef140271d96fade"
[[override]]
name = "github.com/jonboulle/clockwork"
revision = "e3653ace2d63753697e0e5b07b9393971c0bba9d"
......
......@@ -17,9 +17,9 @@ var (
func TestIssue17MergeWithOverwrite(t *testing.T) {
var something map[string]interface{}
if err := json.Unmarshal([]byte(request), &something); err != nil {
t.Errorf("Error while Unmarshalling maprequest %s", err)
t.Errorf("Error while Unmarshalling maprequest: %s", err)
}
if err := MergeWithOverwrite(&something, maprequest); err != nil {
t.Errorf("Error while merging %s", err)
t.Errorf("Error while merging: %s", err)
}
}
......@@ -21,7 +21,7 @@ func TestIssue23MergeWithOverwrite(t *testing.T) {
if err := MergeWithOverwrite(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created != src.Created {
if !dst.Created.Equal(*src.Created) { //--> https://golang.org/pkg/time/#pkg-overview
t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
}
}
package mergo
import (
"testing"
)
type Foo struct {
Str string
Bslice []byte
}
func TestIssue33Merge(t *testing.T) {
dest := Foo{Str: "a"}
toMerge := Foo{
Str: "b",
Bslice: []byte{1, 2},
}
if err := Merge(&dest, toMerge); err != nil {
t.Errorf("Error while merging: %s", err)
}
// Merge doesn't overwrite an attribute if in destination it doesn't have a zero value.
// In this case, Str isn't a zero value string.
if dest.Str != "a" {
t.Errorf("dest.Str should have not been override as it has a non-zero value: dest.Str(%v) != 'a'", dest.Str)
}
// If we want to override, we must use MergeWithOverwrite or Merge using WithOverride.
if err := Merge(&dest, toMerge, WithOverride); err != nil {
t.Errorf("Error while merging: %s", err)
}
if dest.Str != toMerge.Str {
t.Errorf("dest.Str should have been override: dest.Str(%v) != toMerge.Str(%v)", dest.Str, toMerge.Str)
}
}
package mergo
import (
"testing"
"time"
)
type structWithoutTimePointer struct {
Created time.Time
}
func TestIssue38Merge(t *testing.T) {
dst := structWithoutTimePointer{
time.Now(),
}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := structWithoutTimePointer{
expected,
}
if err := Merge(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created == src.Created {
t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
}
}
func TestIssue38MergeEmptyStruct(t *testing.T) {
dst := structWithoutTimePointer{}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := structWithoutTimePointer{
expected,
}
if err := Merge(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created == src.Created {
t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
}
}
func TestIssue38MergeWithOverwrite(t *testing.T) {
dst := structWithoutTimePointer{
time.Now(),
}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := structWithoutTimePointer{
expected,
}
if err := MergeWithOverwrite(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created != src.Created {
t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
}
}
package mergo
import (
"testing"
"time"
)
type testStruct struct {
time.Duration
}
func TestIssue50Merge(t *testing.T) {
to := testStruct{}
from := testStruct{}
if err := Merge(&to, from); err != nil {
t.Fail()
}
}
package mergo
import (
"reflect"
"testing"
"time"
)
type structWithTime struct {
Birth time.Time
}
type timeTransfomer struct {
overwrite bool
}
func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
if typ == reflect.TypeOf(time.Time{}) {
return func(dst, src reflect.Value) error {
if dst.CanSet() {
if t.overwrite {
isZero := src.MethodByName("IsZero")
result := isZero.Call([]reflect.Value{})
if !result[0].Bool() {
dst.Set(src)
}
} else {
isZero := dst.MethodByName("IsZero")
result := isZero.Call([]reflect.Value{})
if result[0].Bool() {
dst.Set(src)
}
}
}
return nil
}
}
return nil
}
func TestOverwriteZeroSrcTime(t *testing.T) {
now := time.Now()
dst := structWithTime{now}
src := structWithTime{}
if err := MergeWithOverwrite(&dst, src); err != nil {
t.FailNow()
}
if !dst.Birth.IsZero() {
t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
}
}
func TestOverwriteZeroSrcTimeWithTransformer(t *testing.T) {
now := time.Now()
dst := structWithTime{now}
src := structWithTime{}
if err := MergeWithOverwrite(&dst, src, WithTransformers(timeTransfomer{true})); err != nil {
t.FailNow()
}
if dst.Birth.IsZero() {
t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
}
}
func TestOverwriteZeroDstTime(t *testing.T) {
now := time.Now()
dst := structWithTime{}
src := structWithTime{now}
if err := MergeWithOverwrite(&dst, src); err != nil {
t.FailNow()
}
if dst.Birth.IsZero() {
t.Fatalf("dst should have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{})
}
}
func TestZeroDstTime(t *testing.T) {
now := time.Now()
dst := structWithTime{}
src := structWithTime{now}
if err := Merge(&dst, src); err != nil {
t.FailNow()
}
if !dst.Birth.IsZero() {
t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{})
}
}
func TestZeroDstTimeWithTransformer(t *testing.T) {
now := time.Now()
dst := structWithTime{}
src := structWithTime{now}
if err := Merge(&dst, src, WithTransformers(timeTransfomer{})); err != nil {
t.FailNow()
}
if dst.Birth.IsZero() {
t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
}
}
package mergo
import (
"reflect"
"testing"
)
func TestIssue61MergeNilMap(t *testing.T) {
type T struct {
I map[string][]string
}
t1 := T{}
t2 := T{I: map[string][]string{"hi": {"there"}}}
if err := Merge(&t1, t2); err != nil {
t.Fail()
}
if !reflect.DeepEqual(t2, T{I: map[string][]string{"hi": {"there"}}}) {
t.FailNow()
}
}
package mergo
import (
"testing"
)
type Student struct {
Name string
Books []string
}
var testData = []struct {
S1 Student
S2 Student
ExpectedSlice []string
}{
{Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"a", "B"}},
{Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}},
{Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}},
{Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}},
}
func TestIssue64MergeSliceWithOverride(t *testing.T) {
for _, data := range testData {
err := Merge(&data.S2, data.S1, WithOverride)
if err != nil {
t.Errorf("Error while merging %s", err)
}
if len(data.S2.Books) != len(data.ExpectedSlice) {
t.Fatalf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice))
}
for i, val := range data.S2.Books {
if val != data.ExpectedSlice[i] {
t.Fatalf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val)
}
}
}
}
package mergo
import (
"testing"
)
type PrivateSliceTest66 struct {
PublicStrings []string
privateStrings []string
}
func TestPrivateSlice(t *testing.T) {
p1 := PrivateSliceTest66{
PublicStrings: []string{"one", "two", "three"},
privateStrings: []string{"four", "five"},
}
p2 := PrivateSliceTest66{
PublicStrings: []string{"six", "seven"},
}
if err := Merge(&p1, p2); err != nil {
t.Fatalf("Error during the merge: %v", err)
}
if len(p1.PublicStrings) != 3 {
t.Error("5 elements should be in 'PublicStrings' field")
}
if len(p1.privateStrings) != 2 {
t.Error("2 elements should be in 'privateStrings' field")
}
}
func TestPrivateSliceWithAppendSlice(t *testing.T) {
p1 := PrivateSliceTest66{
PublicStrings: []string{"one", "two", "three"},
privateStrings: []string{"four", "five"},
}
p2 := PrivateSliceTest66{
PublicStrings: []string{"six", "seven"},
}
if err := Merge(&p1, p2, WithAppendSlice); err != nil {
t.Fatalf("Error during the merge: %v", err)
}
if len(p1.PublicStrings) != 5 {
t.Error("5 elements should be in 'PublicStrings' field")
}
if len(p1.privateStrings) != 2 {
t.Error("2 elements should be in 'privateStrings' field")
}
}
package mergo
import (
"testing"
)
type DstStructIssue84 struct {
A int
B int
C int
}
type DstNestedStructIssue84 struct {
A struct {
A int
B int
C int
}
B int
C int
}
func TestIssue84MergeMapWithNilValueToStructWithOverride(t *testing.T) {
p1 := DstStructIssue84{
A: 0, B: 1, C: 2,
}
p2 := map[string]interface{}{
"A": 3, "B": 4, "C": 0,
}
if err := Map(&p1, p2, WithOverride); err != nil {
t.Fatalf("Error during the merge: %v", err)
}
if p1.C != 0 {
t.Error("C field should become '0'")
}
}
func TestIssue84MergeMapWithoutKeyExistsToStructWithOverride(t *testing.T) {
p1 := DstStructIssue84{
A: 0, B: 1, C: 2,
}
p2 := map[string]interface{}{
"A": 3, "B": 4,
}
if err := Map(&p1, p2, WithOverride); err != nil {
t.Fatalf("Error during the merge: %v", err)
}
if p1.C != 2 {
t.Error("C field should be '2'")
}
}
func TestIssue84MergeNestedMapWithNilValueToStructWithOverride(t *testing.T) {
p1 := DstNestedStructIssue84{
A: struct {
A int
B int
C int
}{A: 1, B: 2, C: 0},
B: 0,
C: 2,
}
p2 := map[string]interface{}{
"A": map[string]interface{}{
"A": 0, "B": 0, "C": 5,
}, "B": 4, "C": 0,
}
if err := Map(&p1, p2, WithOverride); err != nil {
t.Fatalf("Error during the merge: %v", err)
}
if p1.B != 4 {
t.Error("A.C field should become '4'")
}
if p1.A.C != 5 {
t.Error("A.C field should become '5'")
}
if p1.A.B != 0 || p1.A.A != 0 {
t.Error("A.A and A.B field should become '0'")
}
}
......@@ -31,7 +31,8 @@ func isExported(field reflect.StructField) bool {
// 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 deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
overwrite := config.Overwrite
if dst.CanAddr() {
addr := dst.UnsafeAddr()
h := 17 * addr
......@@ -61,9 +62,17 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
dstMap[fieldName] = src.Field(i).Interface()
}
}
case reflect.Ptr:
if dst.IsNil() {
v := reflect.New(dst.Type().Elem())
dst.Set(v)
}
dst = dst.Elem()
fallthrough
case reflect.Struct:
srcMap := src.Interface().(map[string]interface{})
for key := range srcMap {
config.overwriteWithEmptyValue = true
srcValue := srcMap[key]
fieldName := changeInitialCase(key, unicode.ToUpper)
dstElement := dst.FieldByName(fieldName)
......@@ -85,21 +94,24 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
srcKind = reflect.Ptr
}
}
if !srcElement.IsValid() {
continue
}
if srcKind == dstKind {
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else {
if srcKind == reflect.Map {
if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
return
}
} else {
return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
} else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else if srcKind == reflect.Map {
if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else {
return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
}
}
}
......@@ -117,28 +129,35 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
// doesn't apply if dst is a map.
// This is separated method from Merge because it is cleaner and it keeps sane
// semantics: merging equal types, mapping different (restricted) types.
func Map(dst, src interface{}) error {
return _map(dst, src, false)
func Map(dst, src interface{}, opts ...func(*Config)) error {
return _map(dst, src, opts...)
}
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
// non-empty src attribute values.
func MapWithOverwrite(dst, src interface{}) error {
return _map(dst, src, true)
// Deprecated: Use Map(…) with WithOverride
func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
return _map(dst, src, append(opts, WithOverride)...)
}
func _map(dst, src interface{}, overwrite bool) error {
func _map(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
}
// To be friction-less, we redirect equal-type arguments
// to deepMerge. Only because arguments can be anything.
if vSrc.Kind() == vDst.Kind() {
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}
switch vSrc.Kind() {
case reflect.Struct:
......@@ -152,5 +171,5 @@ func _map(dst, src interface{}, overwrite bool) error {
default:
return ErrNotSupported
}
return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}
......@@ -9,13 +9,41 @@
package mergo
import (
"fmt"
"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 && dst.Field(i).Kind() == reflect.Struct {
exported = exported || hasExportedField(dst.Field(i))
} else {
exported = exported || len(field.PkgPath) == 0
}
}
return
}
type Config struct {
Overwrite bool
AppendSlice bool
Transformers Transformers
overwriteWithEmptyValue bool
}
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
overwriteWithEmptySrc := config.overwriteWithEmptyValue
config.overwriteWithEmptyValue = false
if !src.IsValid() {
return
}
......@@ -32,14 +60,31 @@ 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 && !isEmptyValue(dst) {
if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
err = fn(dst, src)
return
}
}
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, overwrite); err != nil {
return
if hasExportedField(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
}
}
} else {
if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
dst.Set(src)
}
}
case reflect.Map:
if dst.IsNil() && !src.IsNil() {
dst.Set(reflect.MakeMap(dst.Type()))
}
for _, key := range src.MapKeys() {
srcElement := src.MapIndex(key)
if !srcElement.IsValid() {
......@@ -47,7 +92,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
}
dstElement := dst.MapIndex(key)
switch srcElement.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
if srcElement.IsNil() {
continue
}
......@@ -62,32 +107,94 @@ 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 {
srcMapElm := srcElement
dstMapElm := dstElement
if srcMapElm.CanInterface() {
srcMapElm = reflect.ValueOf(srcMapElm.Interface())
if dstMapElm.IsValid() {
dstMapElm = reflect.ValueOf(dstMapElm.Interface())
}
}
if err = deepMerge(dstMapElm, srcMapElm, 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())