Codebase list golang-github-imdario-mergo / 729b59b
Merge tag 'upstream/0.2.2' Upstream version 0.2.2 Tim Potter 7 years ago
5 changed file(s) with 321 addition(s) and 44 deletion(s). Raw diff Collapse all Expand all
1010 It is ready for production use. It works fine after extensive use in the wild.
1111
1212 [![Build Status][1]][2]
13 [![GoDoc](https://godoc.org/github.com/imdario/mergo?status.svg)](https://godoc.org/github.com/imdario/mergo)
13 [![GoDoc][3]][4]
14 [![GoCard][5]][6]
1415
1516 [1]: https://travis-ci.org/imdario/mergo.png
1617 [2]: https://travis-ci.org/imdario/mergo
18 [3]: https://godoc.org/github.com/imdario/mergo?status.svg
19 [4]: https://godoc.org/github.com/imdario/mergo
20 [5]: https://goreportcard.com/badge/imdario/mergo
21 [6]: https://goreportcard.com/report/github.com/imdario/mergo
1722
1823 ### Important note
1924
20 Mergo is intended to merge **only** zero value fields on destination. Since April 6th it works like this. Before it didn't work properly, causing some random overwrites. After some issues and PRs I found it didn't merge as I designed it. Thanks to [imdario/mergo#8](https://github.com/imdario/mergo/pull/8) overwriting functions were added and the wrong behavior was clearly detected.
25 Mergo is intended to assign **only** zero value fields on destination with source value. Since April 6th it works like this. Before it didn't work properly, causing some random overwrites. After some issues and PRs I found it didn't merge as I designed it. Thanks to [imdario/mergo#8](https://github.com/imdario/mergo/pull/8) overwriting functions were added and the wrong behavior was clearly detected.
2126
2227 If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0).
2328
2429 ### Mergo in the wild
2530
31 - [docker/docker](https://github.com/docker/docker/)
2632 - [GoogleCloudPlatform/kubernetes](https://github.com/GoogleCloudPlatform/kubernetes)
33 - [imdario/zas](https://github.com/imdario/zas)
2734 - [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
2835 - [EagerIO/Stout](https://github.com/EagerIO/Stout)
2936 - [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
3239 - [casualjim/exeggutor](https://github.com/casualjim/exeggutor)
3340 - [divshot/gitling](https://github.com/divshot/gitling)
3441 - [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl)
42 - [andrerocker/deploy42](https://github.com/andrerocker/deploy42)
43 - [elwinar/rambler](https://github.com/elwinar/rambler)
44 - [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman)
45 - [jfbus/impressionist](https://github.com/jfbus/impressionist)
46 - [Jmeyering/zealot](https://github.com/Jmeyering/zealot)
47 - [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host)
48 - [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go)
49 - [thoas/picfit](https://github.com/thoas/picfit)
50 - [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
51 - [jnuthong/item_search](https://github.com/jnuthong/item_search)
3552
3653 ## Installation
3754
89106
90107 fmt.Println(dest)
91108 // Will print
92 // {one 2}
109 // {two 2}
93110 }
94111 ```
95112
0 package mergo
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 request = `{"timestamp":null, "name": "foo"}`
9 maprequest = map[string]interface{}{
10 "timestamp": nil,
11 "name": "foo",
12 "newStuff": "foo",
13 }
14 )
15
16 func TestIssue17MergeWithOverwrite(t *testing.T) {
17 var something map[string]interface{}
18 if err := json.Unmarshal([]byte(request), &something); err != nil {
19 t.Errorf("Error while Unmarshalling maprequest %s", err)
20 }
21 if err := MergeWithOverwrite(&something, maprequest); err != nil {
22 t.Errorf("Error while merging %s", err)
23 }
24 }
120120 return _map(dst, src, false)
121121 }
122122
123 // MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by
124 // non-empty src attribute values.
123125 func MapWithOverwrite(dst, src interface{}) error {
124126 return _map(dst, src, true)
125127 }
4545 continue
4646 }
4747 dstElement := dst.MapIndex(key)
48 switch reflect.TypeOf(srcElement.Interface()).Kind() {
49 case reflect.Struct:
48 switch srcElement.Kind() {
49 case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
50 if srcElement.IsNil() {
51 continue
52 }
5053 fallthrough
51 case reflect.Map:
52 if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
53 return
54 default:
55 if !srcElement.CanInterface() {
56 continue
57 }
58 switch reflect.TypeOf(srcElement.Interface()).Kind() {
59 case reflect.Struct:
60 fallthrough
61 case reflect.Ptr:
62 fallthrough
63 case reflect.Map:
64 if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
65 return
66 }
5467 }
5568 }
5669 if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
70 if dst.IsNil() {
71 dst.Set(reflect.MakeMap(dst.Type()))
72 }
5773 dst.SetMapIndex(key, srcElement)
5874 }
5975 }
7793 return
7894 }
7995
80 // Merge sets fields' values in dst from src if they have a zero
81 // value of their type.
82 // dst and src must be valid same-type structs and dst must be
83 // a pointer to struct.
84 // It won't merge unexported (private) fields and will do recursively
85 // any exported field.
96 // Merge will fill any empty for value type attributes on the dst struct using corresponding
97 // src attributes if they themselves are not empty. dst and src must be valid same-type structs
98 // and dst must be a pointer to struct.
99 // It won't merge unexported (private) fields and will do recursively any exported field.
86100 func Merge(dst, src interface{}) error {
87101 return merge(dst, src, false)
88102 }
89103
104 // MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
105 // non-empty src attribute values.
90106 func MergeWithOverwrite(dst, src interface{}) error {
91107 return merge(dst, src, true)
92108 }
77 import (
88 "reflect"
99 "testing"
10 "time"
11
12 "gopkg.in/yaml.v1"
1013 )
1114
1215 type simpleTest struct {
1619 type complexTest struct {
1720 St simpleTest
1821 sz int
19 Id string
22 ID string
2023 }
2124
2225 type moreComplextText struct {
98101
99102 func TestComplexStruct(t *testing.T) {
100103 a := complexTest{}
101 a.Id = "athing"
104 a.ID = "athing"
102105 b := complexTest{simpleTest{42}, 1, "bthing"}
103106 if err := Merge(&a, b); err != nil {
104107 t.FailNow()
109112 if a.sz == 1 {
110113 t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz)
111114 }
112 if a.Id == b.Id {
113 t.Fatalf("a's field Id merged unexpectedly: a.Id(%s) == b.Id(%s)", a.Id, b.Id)
115 if a.ID == b.ID {
116 t.Fatalf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID)
114117 }
115118 }
116119
241244
242245 func TestMapsWithOverwrite(t *testing.T) {
243246 m := map[string]simpleTest{
244 "a": simpleTest{}, // overwritten by 16
245 "b": simpleTest{42}, // not overwritten by empty value
246 "c": simpleTest{13}, // overwritten by 12
247 "d": simpleTest{61},
247 "a": {}, // overwritten by 16
248 "b": {42}, // not overwritten by empty value
249 "c": {13}, // overwritten by 12
250 "d": {61},
248251 }
249252 n := map[string]simpleTest{
250 "a": simpleTest{16},
251 "b": simpleTest{},
252 "c": simpleTest{12},
253 "e": simpleTest{14},
253 "a": {16},
254 "b": {},
255 "c": {12},
256 "e": {14},
254257 }
255258 expect := map[string]simpleTest{
256 "a": simpleTest{16},
257 "b": simpleTest{},
258 "c": simpleTest{12},
259 "d": simpleTest{61},
260 "e": simpleTest{14},
259 "a": {16},
260 "b": {},
261 "c": {12},
262 "d": {61},
263 "e": {14},
261264 }
262265
263266 if err := MergeWithOverwrite(&m, n); err != nil {
271274
272275 func TestMaps(t *testing.T) {
273276 m := map[string]simpleTest{
274 "a": simpleTest{},
275 "b": simpleTest{42},
276 "c": simpleTest{13},
277 "d": simpleTest{61},
277 "a": {},
278 "b": {42},
279 "c": {13},
280 "d": {61},
278281 }
279282 n := map[string]simpleTest{
280 "a": simpleTest{16},
281 "b": simpleTest{},
282 "c": simpleTest{12},
283 "e": simpleTest{14},
283 "a": {16},
284 "b": {},
285 "c": {12},
286 "e": {14},
284287 }
285288 expect := map[string]simpleTest{
286 "a": simpleTest{0},
287 "b": simpleTest{42},
288 "c": simpleTest{13},
289 "d": simpleTest{61},
290 "e": simpleTest{14},
289 "a": {0},
290 "b": {42},
291 "c": {13},
292 "d": {61},
293 "e": {14},
291294 }
292295
293296 if err := Merge(&m, n); err != nil {
307310 t.Fatalf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value)
308311 }
309312 }
313 <<<<<<< HEAD
314 =======
315
316 func TestYAMLMaps(t *testing.T) {
317 thing := loadYAML("testdata/thing.yml")
318 license := loadYAML("testdata/license.yml")
319 ft := thing["fields"].(map[interface{}]interface{})
320 fl := license["fields"].(map[interface{}]interface{})
321 expectedLength := len(ft) + len(fl)
322 if err := Merge(&license, thing); err != nil {
323 t.Fatal(err.Error())
324 }
325 currentLength := len(license["fields"].(map[interface{}]interface{}))
326 if currentLength != expectedLength {
327 t.Fatalf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength)
328 }
329 fields := license["fields"].(map[interface{}]interface{})
330 if _, ok := fields["id"]; !ok {
331 t.Fatalf(`thing not merged in license properly, license must have a new id field from thing`)
332 }
333 }
334
335 func TestTwoPointerValues(t *testing.T) {
336 a := &simpleTest{}
337 b := &simpleTest{42}
338 if err := Merge(a, b); err != nil {
339 t.Fatalf(`Boom. You crossed the streams: %s`, err)
340 }
341 }
342
343 func TestMap(t *testing.T) {
344 a := complexTest{}
345 a.ID = "athing"
346 c := moreComplextText{a, simpleTest{}, simpleTest{}}
347 b := map[string]interface{}{
348 "ct": map[string]interface{}{
349 "st": map[string]interface{}{
350 "value": 42,
351 },
352 "sz": 1,
353 "id": "bthing",
354 },
355 "st": &simpleTest{144}, // Mapping a reference
356 "zt": simpleTest{299}, // Mapping a missing field (zt doesn't exist)
357 "nt": simpleTest{3},
358 }
359 if err := Map(&c, b); err != nil {
360 t.FailNow()
361 }
362 m := b["ct"].(map[string]interface{})
363 n := m["st"].(map[string]interface{})
364 o := b["st"].(*simpleTest)
365 p := b["nt"].(simpleTest)
366 if c.Ct.St.Value != 42 {
367 t.Fatalf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"])
368 }
369 if c.St.Value != 144 {
370 t.Fatalf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value)
371 }
372 if c.Nt.Value != 3 {
373 t.Fatalf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value)
374 }
375 if c.Ct.sz == 1 {
376 t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"])
377 }
378 if c.Ct.ID == m["id"] {
379 t.Fatalf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"])
380 }
381 }
382
383 func TestSimpleMap(t *testing.T) {
384 a := simpleTest{}
385 b := map[string]interface{}{
386 "value": 42,
387 }
388 if err := Map(&a, b); err != nil {
389 t.FailNow()
390 }
391 if a.Value != 42 {
392 t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"])
393 }
394 }
395
396 type pointerMapTest struct {
397 A int
398 hidden int
399 B *simpleTest
400 }
401
402 func TestBackAndForth(t *testing.T) {
403 pt := pointerMapTest{42, 1, &simpleTest{66}}
404 m := make(map[string]interface{})
405 if err := Map(&m, pt); err != nil {
406 t.FailNow()
407 }
408 var (
409 v interface{}
410 ok bool
411 )
412 if v, ok = m["a"]; v.(int) != pt.A || !ok {
413 t.Fatalf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A)
414 }
415 if v, ok = m["b"]; !ok {
416 t.Fatalf("pt not merged in properly: B is missing in m")
417 }
418 var st *simpleTest
419 if st = v.(*simpleTest); st.Value != 66 {
420 t.Fatalf("something went wrong while mapping pt on m, B wasn't copied")
421 }
422 bpt := pointerMapTest{}
423 if err := Map(&bpt, m); err != nil {
424 t.Fatal(err)
425 }
426 if bpt.A != pt.A {
427 t.Fatalf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A)
428 }
429 if bpt.hidden == pt.hidden {
430 t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden)
431 }
432 if bpt.B.Value != pt.B.Value {
433 t.Fatalf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value)
434 }
435 }
436
437 type structWithTimePointer struct {
438 Birth *time.Time
439 }
440
441 func TestTime(t *testing.T) {
442 now := time.Now()
443 dataStruct := structWithTimePointer{
444 Birth: &now,
445 }
446 dataMap := map[string]interface{}{
447 "Birth": &now,
448 }
449 b := structWithTimePointer{}
450 if err := Merge(&b, dataStruct); err != nil {
451 t.FailNow()
452 }
453 if b.Birth.IsZero() {
454 t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
455 }
456 if b.Birth != dataStruct.Birth {
457 t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
458 }
459 b = structWithTimePointer{}
460 if err := Map(&b, dataMap); err != nil {
461 t.FailNow()
462 }
463 if b.Birth.IsZero() {
464 t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"])
465 }
466 }
467
468 type simpleNested struct {
469 A int
470 }
471
472 type structWithNestedPtrValueMap struct {
473 NestedPtrValue map[string]*simpleNested
474 }
475
476 func TestNestedPtrValueInMap(t *testing.T) {
477 src := &structWithNestedPtrValueMap{
478 NestedPtrValue: map[string]*simpleNested{
479 "x": {
480 A: 1,
481 },
482 },
483 }
484 dst := &structWithNestedPtrValueMap{
485 NestedPtrValue: map[string]*simpleNested{
486 "x": {},
487 },
488 }
489 if err := Map(dst, src); err != nil {
490 t.FailNow()
491 }
492 if dst.NestedPtrValue["x"].A == 0 {
493 t.Fatalf("Nested Ptr value not merged in properly: dst.NestedPtrValue[\"x\"].A(%v) != src.NestedPtrValue[\"x\"].A(%v)", dst.NestedPtrValue["x"].A, src.NestedPtrValue["x"].A)
494 }
495 }
496
497 func loadYAML(path string) (m map[string]interface{}) {
498 m = make(map[string]interface{})
499 raw, _ := ioutil.ReadFile(path)
500 _ = yaml.Unmarshal(raw, &m)
501 return
502 }
503
504 type structWithMap struct {
505 m map[string]structWithUnexportedProperty
506 }
507
508 type structWithUnexportedProperty struct {
509 s string
510 }
511
512 func TestUnexportedProperty(t *testing.T) {
513 a := structWithMap{map[string]structWithUnexportedProperty{
514 "key": structWithUnexportedProperty{"hello"},
515 }}
516 b := structWithMap{map[string]structWithUnexportedProperty{
517 "key": structWithUnexportedProperty{"hi"},
518 }}
519 defer func() {
520 if r := recover(); r != nil {
521 t.Errorf("Should not have panicked")
522 }
523 }()
524 Merge(&a, b)
525 }
526 >>>>>>> upstream/0.2.2