Codebase list golang-github-imdario-mergo / upstream/latest
New upstream version 0.3.8 Dmitry Smirnov 4 years ago
9 changed file(s) with 443 addition(s) and 14 deletion(s). Raw diff Collapse all Expand all
0 # These are supported funding model platforms
1
2 ko_fi: dariocc
3 custom: https://beerpay.io/imdario/mergo
33 - go get golang.org/x/tools/cmd/cover
44 - go get github.com/mattn/goveralls
55 script:
6 - go test -race -v ./...
7 after_script:
68 - $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN
1212 [![Build Status][1]][2]
1313 [![Coverage Status][7]][8]
1414 [![Sourcegraph][9]][10]
15 [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield)
1516
1617 [1]: https://travis-ci.org/imdario/mergo.png
1718 [2]: https://travis-ci.org/imdario/mergo
2627
2728 ### Latest release
2829
29 [Release v0.3.4](https://github.com/imdario/mergo/releases/tag/v0.3.4).
30 [Release v0.3.7](https://github.com/imdario/mergo/releases/tag/v0.3.7).
3031
3132 ### Important note
3233
216217
217218 Written by [Dario Castañé](http://dario.im).
218219
220 ## Top Contributors
221
222 [![0](https://sourcerer.io/fame/imdario/imdario/mergo/images/0)](https://sourcerer.io/fame/imdario/imdario/mergo/links/0)
223 [![1](https://sourcerer.io/fame/imdario/imdario/mergo/images/1)](https://sourcerer.io/fame/imdario/imdario/mergo/links/1)
224 [![2](https://sourcerer.io/fame/imdario/imdario/mergo/images/2)](https://sourcerer.io/fame/imdario/imdario/mergo/links/2)
225 [![3](https://sourcerer.io/fame/imdario/imdario/mergo/images/3)](https://sourcerer.io/fame/imdario/imdario/mergo/links/3)
226 [![4](https://sourcerer.io/fame/imdario/imdario/mergo/images/4)](https://sourcerer.io/fame/imdario/imdario/mergo/links/4)
227 [![5](https://sourcerer.io/fame/imdario/imdario/mergo/images/5)](https://sourcerer.io/fame/imdario/imdario/mergo/links/5)
228 [![6](https://sourcerer.io/fame/imdario/imdario/mergo/images/6)](https://sourcerer.io/fame/imdario/imdario/mergo/links/6)
229 [![7](https://sourcerer.io/fame/imdario/imdario/mergo/images/7)](https://sourcerer.io/fame/imdario/imdario/mergo/links/7)
230
231
219232 ## License
220233
221234 [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE).
235
236
237 [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large)
0 package mergo
1
2 import (
3 "encoding/json"
4 "testing"
5 )
6
7 var (
8 data = `{"FirstSlice":[], "SecondSlice": null}`
9 )
10
11 type settings struct {
12 FirstSlice []string `json:"FirstSlice"`
13 SecondSlice []string `json:"SecondSlice"`
14 }
15
16 func TestIssue125MergeWithOverwrite(t *testing.T) {
17
18 defaultSettings := settings{
19 FirstSlice: []string{},
20 SecondSlice: []string{},
21 }
22
23 var something settings
24 if err := json.Unmarshal([]byte(data), &something); err != nil {
25 t.Errorf("Error while Unmarshalling maprequest: %s", err)
26 }
27 if err := Merge(&something, defaultSettings, WithOverrideEmptySlice); err != nil {
28 t.Errorf("Error while merging: %s", err)
29 }
30 if something.FirstSlice == nil {
31 t.Error("Invalid merging first slice")
32 }
33 if something.SecondSlice == nil {
34 t.Error("Invalid merging second slice")
35 }
36 }
0 package mergo
1
2 import (
3 "testing"
4 )
5
6 type DstStructIssue84 struct {
7 A int
8 B int
9 C int
10 }
11
12 type DstNestedStructIssue84 struct {
13 A struct {
14 A int
15 B int
16 C int
17 }
18 B int
19 C int
20 }
21
22 func TestIssue84MergeMapWithNilValueToStructWithOverride(t *testing.T) {
23 p1 := DstStructIssue84{
24 A: 0, B: 1, C: 2,
25 }
26 p2 := map[string]interface{}{
27 "A": 3, "B": 4, "C": 0,
28 }
29 if err := Map(&p1, p2, WithOverride); err != nil {
30 t.Fatalf("Error during the merge: %v", err)
31 }
32 if p1.C != 0 {
33 t.Error("C field should become '0'")
34 }
35 }
36
37 func TestIssue84MergeMapWithoutKeyExistsToStructWithOverride(t *testing.T) {
38 p1 := DstStructIssue84{
39 A: 0, B: 1, C: 2,
40 }
41 p2 := map[string]interface{}{
42 "A": 3, "B": 4,
43 }
44 if err := Map(&p1, p2, WithOverride); err != nil {
45 t.Fatalf("Error during the merge: %v", err)
46 }
47 if p1.C != 2 {
48 t.Error("C field should be '2'")
49 }
50 }
51
52 func TestIssue84MergeNestedMapWithNilValueToStructWithOverride(t *testing.T) {
53 p1 := DstNestedStructIssue84{
54 A: struct {
55 A int
56 B int
57 C int
58 }{A: 1, B: 2, C: 0},
59 B: 0,
60 C: 2,
61 }
62 p2 := map[string]interface{}{
63 "A": map[string]interface{}{
64 "A": 0, "B": 0, "C": 5,
65 }, "B": 4, "C": 0,
66 }
67 if err := Map(&p1, p2, WithOverride); err != nil {
68 t.Fatalf("Error during the merge: %v", err)
69 }
70 if p1.B != 4 {
71 t.Error("A.C field should become '4'")
72 }
73
74 if p1.A.C != 5 {
75 t.Error("A.C field should become '5'")
76 }
77
78 if p1.A.B != 0 || p1.A.A != 0 {
79 t.Error("A.A and A.B field should become '0'")
80 }
81 }
7171 case reflect.Struct:
7272 srcMap := src.Interface().(map[string]interface{})
7373 for key := range srcMap {
74 config.overwriteWithEmptyValue = true
7475 srcValue := srcMap[key]
7576 fieldName := changeInitialCase(key, unicode.ToUpper)
7677 dstElement := dst.FieldByName(fieldName)
88 package mergo
99
1010 import (
11 "fmt"
1112 "reflect"
1213 )
1314
2425 }
2526
2627 type Config struct {
27 Overwrite bool
28 AppendSlice bool
29 Transformers Transformers
28 Overwrite bool
29 AppendSlice bool
30 TypeCheck bool
31 Transformers Transformers
32 overwriteWithEmptyValue bool
33 overwriteSliceWithEmptyValue bool
3034 }
3135
3236 type Transformers interface {
3842 // short circuiting on recursive types.
3943 func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
4044 overwrite := config.Overwrite
45 typeCheck := config.TypeCheck
46 overwriteWithEmptySrc := config.overwriteWithEmptyValue
47 overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
48 config.overwriteWithEmptyValue = false
4149
4250 if !src.IsValid() {
4351 return
7280 }
7381 }
7482 } else {
75 if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
83 if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
7684 dst.Set(src)
7785 }
7886 }
123131 dstSlice = reflect.ValueOf(dstElement.Interface())
124132 }
125133
126 if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
134 if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
135 if typeCheck && srcSlice.Type() != dstSlice.Type() {
136 return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
137 }
127138 dstSlice = srcSlice
128139 } else if config.AppendSlice {
140 if srcSlice.Type() != dstSlice.Type() {
141 return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
142 }
129143 dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
130144 }
131145 dst.SetMapIndex(key, dstSlice)
132146 }
133147 }
134 if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
148 if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
135149 continue
136150 }
137151
138 if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) {
152 if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) {
139153 if dst.IsNil() {
140154 dst.Set(reflect.MakeMap(dst.Type()))
141155 }
146160 if !dst.CanSet() {
147161 break
148162 }
149 if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
163 if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
150164 dst.Set(src)
151165 } else if config.AppendSlice {
166 if src.Type() != dst.Type() {
167 return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
168 }
152169 dst.Set(reflect.AppendSlice(dst, src))
153170 }
154171 case reflect.Ptr:
157174 if src.IsNil() {
158175 break
159176 }
160 if src.Kind() != reflect.Interface {
177
178 if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) {
161179 if dst.IsNil() || overwrite {
162180 if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
163181 dst.Set(src)
164182 }
183 }
184 break
185 }
186
187 if src.Kind() != reflect.Interface {
188 if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
189 if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
190 dst.Set(src)
191 }
165192 } else if src.Kind() == reflect.Ptr {
166193 if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
167194 return
183210 return
184211 }
185212 default:
186 if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
213 if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) {
187214 dst.Set(src)
188215 }
189216 }
217
190218 return
191219 }
192220
198226 return merge(dst, src, opts...)
199227 }
200228
201 // MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
229 // MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
202230 // non-empty src attribute values.
203231 // Deprecated: use Merge(…) with WithOverride
204232 func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
217245 config.Overwrite = true
218246 }
219247
220 // WithAppendSlice will make merge append slices instead of overwriting it
248 // WithOverride will make merge override empty dst slice with empty src slice.
249 func WithOverrideEmptySlice(config *Config) {
250 config.overwriteSliceWithEmptyValue = true
251 }
252
253 // WithAppendSlice will make merge append slices instead of overwriting it.
221254 func WithAppendSlice(config *Config) {
222255 config.AppendSlice = true
256 }
257
258 // WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
259 func WithTypeCheck(config *Config) {
260 config.TypeCheck = true
223261 }
224262
225263 func merge(dst, src interface{}, opts ...func(*Config)) error {
0 package mergo
1
2 import (
3 "net/http"
4 "net/http/httptest"
5 "testing"
6 )
7
8 type ifaceTypesTest struct {
9 N int
10 Handler http.Handler
11 }
12
13 type ifaceTypesHandler int
14
15 func (*ifaceTypesHandler) ServeHTTP(rw http.ResponseWriter, _ *http.Request) {
16 rw.Header().Set("Test", "ifaceTypesHandler")
17 }
18
19 func TestMergeInterfaceWithDifferentConcreteTypes(t *testing.T) {
20 dst := ifaceTypesTest{
21 Handler: new(ifaceTypesHandler),
22 }
23
24 src := ifaceTypesTest{
25 N: 42,
26 Handler: http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
27 rw.Header().Set("Test", "handlerFunc")
28 }),
29 }
30
31 if err := Merge(&dst, src); err != nil {
32 t.Errorf("Error while merging %s", err)
33 }
34
35 rw := httptest.NewRecorder()
36 dst.Handler.ServeHTTP(rw, nil)
37
38 if got, want := rw.Header().Get("Test"), "ifaceTypesHandler"; got != want {
39 t.Errorf("Handler not merged in properly: got %q header value %q, want %q", "Test", got, want)
40 }
41 }
77 import (
88 "io/ioutil"
99 "reflect"
10 "strings"
1011 "testing"
1112 "time"
13
14 "github.com/stretchr/testify/assert"
1215
1316 "gopkg.in/yaml.v2"
1417 )
293296 testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}, WithAppendSlice)
294297 testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}, WithAppendSlice)
295298 testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, WithAppendSlice)
299 testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, WithAppendSlice, WithOverride)
296300 testSlice(t, []int{1}, []int{}, []int{1}, WithAppendSlice)
297301 testSlice(t, []int{1}, nil, []int{1}, WithAppendSlice)
298302 }
344348 func TestMapsWithOverwrite(t *testing.T) {
345349 m := map[string]simpleTest{
346350 "a": {}, // overwritten by 16
347 "b": {42}, // not overwritten by empty value
351 "b": {42}, // overwritten by 0, as map Value is not addressable and it doesn't check for b is set or not set in `n`
348352 "c": {13}, // overwritten by 12
349353 "d": {61},
350354 }
371375 }
372376 }
373377
378 func TestMapWithEmbeddedStructPointer(t *testing.T) {
379 m := map[string]*simpleTest{
380 "a": {}, // overwritten by 16
381 "b": {42}, // not overwritten by empty value
382 "c": {13}, // overwritten by 12
383 "d": {61},
384 }
385 n := map[string]*simpleTest{
386 "a": {16},
387 "b": {},
388 "c": {12},
389 "e": {14},
390 }
391 expect := map[string]*simpleTest{
392 "a": {16},
393 "b": {42},
394 "c": {12},
395 "d": {61},
396 "e": {14},
397 }
398
399 if err := Merge(&m, n, WithOverride); err != nil {
400 t.Fatalf(err.Error())
401 }
402
403 assert.Equalf(t, expect, m, "Test Failed")
404 if !reflect.DeepEqual(m, expect) {
405 t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
406 }
407 }
408
409 func TestMergeUsingStructAndMap(t *testing.T) {
410 type multiPtr struct {
411 Text string
412 Number int
413 }
414 type final struct {
415 Msg1 string
416 Msg2 string
417 }
418 type params struct {
419 Name string
420 Multi *multiPtr
421 Final *final
422 }
423 type config struct {
424 Foo string
425 Bar string
426 Params *params
427 }
428
429 cases := []struct {
430 name string
431 overwrite bool
432 changes *config
433 target *config
434 output *config
435 }{
436 {
437 name: "Should overwrite values in target for non-nil values in source",
438 overwrite: true,
439 changes: &config{
440 Bar: "from changes",
441 Params: &params{
442 Final: &final{
443 Msg1: "from changes",
444 Msg2: "from changes",
445 },
446 },
447 },
448 target: &config{
449 Foo: "from target",
450 Params: &params{
451 Name: "from target",
452 Multi: &multiPtr{
453 Text: "from target",
454 Number: 5,
455 },
456 Final: &final{
457 Msg1: "from target",
458 Msg2: "",
459 },
460 },
461 },
462 output: &config{
463 Foo: "from target",
464 Bar: "from changes",
465 Params: &params{
466 Name: "from target",
467 Multi: &multiPtr{
468 Text: "from target",
469 Number: 5,
470 },
471 Final: &final{
472 Msg1: "from changes",
473 Msg2: "from changes",
474 },
475 },
476 },
477 },
478 {
479 name: "Should not overwrite values in target for non-nil values in source",
480 overwrite: false,
481 changes: &config{
482 Bar: "from changes",
483 Params: &params{
484 Final: &final{
485 Msg1: "from changes",
486 Msg2: "from changes",
487 },
488 },
489 },
490 target: &config{
491 Foo: "from target",
492 Params: &params{
493 Name: "from target",
494 Multi: &multiPtr{
495 Text: "from target",
496 Number: 5,
497 },
498 Final: &final{
499 Msg1: "from target",
500 Msg2: "",
501 },
502 },
503 },
504 output: &config{
505 Foo: "from target",
506 Bar: "from changes",
507 Params: &params{
508 Name: "from target",
509 Multi: &multiPtr{
510 Text: "from target",
511 Number: 5,
512 },
513 Final: &final{
514 Msg1: "from target",
515 Msg2: "from changes",
516 },
517 },
518 },
519 },
520 }
521
522 for _, tc := range cases {
523 t.Run(tc.name, func(t *testing.T) {
524 var err error
525 if tc.overwrite {
526 err = Merge(tc.target, *tc.changes, WithOverride)
527 } else {
528 err = Merge(tc.target, *tc.changes)
529 }
530 if err != nil {
531 t.Error(err)
532 }
533 if !reflect.DeepEqual(tc.target, tc.output) {
534 t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", tc.target, tc.output)
535 }
536 })
537 }
538 }
374539 func TestMaps(t *testing.T) {
375540 m := map[string]simpleTest{
376541 "a": {},
730895 t.Fatalf("dst.C should be true")
731896 }
732897 }
898
899 func TestMergeMapWithInnerSliceOfDifferentType(t *testing.T) {
900 testCases := []struct {
901 name string
902 options []func(*Config)
903 err string
904 }{
905 {
906 "With override and append slice",
907 []func(*Config){WithOverride, WithAppendSlice},
908 "cannot append two slices with different type",
909 },
910 {
911 "With override and type check",
912 []func(*Config){WithOverride, WithTypeCheck},
913 "cannot override two slices with different type",
914 },
915 }
916 for _, tc := range testCases {
917 t.Run(tc.name, func(t *testing.T) {
918 src := map[string]interface{}{
919 "foo": []string{"a", "b"},
920 }
921 dst := map[string]interface{}{
922 "foo": []int{1, 2},
923 }
924
925 if err := Merge(&src, &dst, tc.options...); err == nil || !strings.Contains(err.Error(), tc.err) {
926 t.Fatalf("expected %q, got %q", tc.err, err)
927 }
928 })
929 }
930 }
931
932 func TestMergeSlicesIsNotSupported(t *testing.T) {
933 src := []string{"a", "b"}
934 dst := []int{1, 2}
935
936 if err := Merge(&src, &dst, WithOverride, WithAppendSlice); err != ErrNotSupported {
937 t.Fatalf("expected %q, got %q", ErrNotSupported, err)
938 }
939 }