New upstream snapshot.
Debian Janitor
2 years ago
0 | version = 1 | |
1 | ||
2 | test_patterns = [ | |
3 | "*_test.go" | |
4 | ] | |
5 | ||
6 | [[analyzers]] | |
7 | name = "go" | |
8 | enabled = true | |
9 | ||
10 | [analyzers.meta] | |
11 | import_path = "github.com/imdario/mergo"⏎ |
0 | 0 | # These are supported funding model platforms |
1 | 1 | |
2 | github: imdario | |
2 | 3 | ko_fi: dariocc |
3 | 4 | custom: https://beerpay.io/imdario/mergo |
0 | 0 | language: go |
1 | arch: | |
2 | - amd64 | |
3 | - ppc64le | |
1 | 4 | install: |
2 | 5 | - go get -t |
3 | 6 | - go get golang.org/x/tools/cmd/cover |
0 | { | |
1 | "go.lintTool": "golangci-lint", | |
2 | "go.lintFlags": [ | |
3 | "--enable-all", | |
4 | "--disable=gomnd" | |
5 | ] | |
6 | }⏎ |
0 | 0 | # Mergo |
1 | 1 | |
2 | A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. | |
3 | ||
4 | Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. | |
5 | ||
6 | ## Status | |
7 | ||
8 | It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild). | |
9 | 2 | |
10 | 3 | [![GoDoc][3]][4] |
11 | [![GoCard][5]][6] | |
4 | [![GitHub release][5]][6] | |
5 | [![GoCard][7]][8] | |
12 | 6 | [![Build Status][1]][2] |
13 | [![Coverage Status][7]][8] | |
14 | [![Sourcegraph][9]][10] | |
15 | [](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield) | |
7 | [![Coverage Status][9]][10] | |
8 | [![Sourcegraph][11]][12] | |
9 | [![FOSSA Status][13]][14] | |
10 | ||
11 | [![GoCenter Kudos][15]][16] | |
16 | 12 | |
17 | 13 | [1]: https://travis-ci.org/imdario/mergo.png |
18 | 14 | [2]: https://travis-ci.org/imdario/mergo |
19 | 15 | [3]: https://godoc.org/github.com/imdario/mergo?status.svg |
20 | 16 | [4]: https://godoc.org/github.com/imdario/mergo |
21 | [5]: https://goreportcard.com/badge/imdario/mergo | |
22 | [6]: https://goreportcard.com/report/github.com/imdario/mergo | |
23 | [7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master | |
24 | [8]: https://coveralls.io/github/imdario/mergo?branch=master | |
25 | [9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg | |
26 | [10]: https://sourcegraph.com/github.com/imdario/mergo?badge | |
27 | ||
28 | ### Latest release | |
29 | ||
30 | [Release v0.3.7](https://github.com/imdario/mergo/releases/tag/v0.3.7). | |
17 | [5]: https://img.shields.io/github/release/imdario/mergo.svg | |
18 | [6]: https://github.com/imdario/mergo/releases | |
19 | [7]: https://goreportcard.com/badge/imdario/mergo | |
20 | [8]: https://goreportcard.com/report/github.com/imdario/mergo | |
21 | [9]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master | |
22 | [10]: https://coveralls.io/github/imdario/mergo?branch=master | |
23 | [11]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg | |
24 | [12]: https://sourcegraph.com/github.com/imdario/mergo?badge | |
25 | [13]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield | |
26 | [14]: https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield | |
27 | [15]: https://search.gocenter.io/api/ui/badge/github.com%2Fimdario%2Fmergo | |
28 | [16]: https://search.gocenter.io/github.com/imdario/mergo | |
29 | ||
30 | A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. | |
31 | ||
32 | Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). | |
33 | ||
34 | Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. | |
35 | ||
36 | ## Status | |
37 | ||
38 | It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild). | |
31 | 39 | |
32 | 40 | ### Important note |
33 | 41 | |
34 | Please keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2) Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code. | |
35 | ||
36 | 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). | |
42 | Please keep in mind that a problematic PR broke [0.3.9](//github.com/imdario/mergo/releases/tag/0.3.9). I reverted it in [0.3.10](//github.com/imdario/mergo/releases/tag/0.3.10), and I consider it stable but not bug-free. Also, this version adds support for go modules. | |
43 | ||
44 | Keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2), Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). I added an optional/variadic argument so that it won't break the existing code. | |
45 | ||
46 | 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 in existing projects after the change (release 0.2.0). | |
37 | 47 | |
38 | 48 | ### Donations |
39 | 49 | |
40 | If Mergo is useful to you, consider buying me a coffee, a beer or making a monthly donation so I can keep building great free software. :heart_eyes: | |
50 | If Mergo is useful to you, consider buying me a coffee, a beer, or making a monthly donation to allow me to keep building great free software. :heart_eyes: | |
41 | 51 | |
42 | 52 | <a href='https://ko-fi.com/B0B58839' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a> |
43 | 53 | [](https://beerpay.io/imdario/mergo) |
86 | 96 | - [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server) |
87 | 97 | - [jnuthong/item_search](https://github.com/jnuthong/item_search) |
88 | 98 | - [bukalapak/snowboard](https://github.com/bukalapak/snowboard) |
89 | ||
90 | ## Installation | |
99 | - [containerssh/containerssh](https://github.com/containerssh/containerssh) | |
100 | - [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser) | |
101 | - [tjpnz/structbot](https://github.com/tjpnz/structbot) | |
102 | ||
103 | ## Install | |
91 | 104 | |
92 | 105 | go get github.com/imdario/mergo |
93 | 106 | |
98 | 111 | |
99 | 112 | ## Usage |
100 | 113 | |
101 | You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are not considered zero values](https://golang.org/ref/spec#The_zero_value) either. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). | |
114 | You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are zero values](https://golang.org/ref/spec#The_zero_value) too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). | |
102 | 115 | |
103 | 116 | ```go |
104 | 117 | if err := mergo.Merge(&dst, src); err != nil { |
124 | 137 | |
125 | 138 | Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values. |
126 | 139 | |
127 | More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo). | |
128 | ||
129 | ### Nice example | |
140 | Here is a nice example: | |
130 | 141 | |
131 | 142 | ```go |
132 | 143 | package main |
174 | 185 | "time" |
175 | 186 | ) |
176 | 187 | |
177 | type timeTransfomer struct { | |
178 | } | |
179 | ||
180 | func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { | |
188 | type timeTransformer struct { | |
189 | } | |
190 | ||
191 | func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { | |
181 | 192 | if typ == reflect.TypeOf(time.Time{}) { |
182 | 193 | return func(dst, src reflect.Value) error { |
183 | 194 | if dst.CanSet() { |
201 | 212 | func main() { |
202 | 213 | src := Snapshot{time.Now()} |
203 | 214 | dest := Snapshot{} |
204 | mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{})) | |
215 | mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{})) | |
205 | 216 | fmt.Println(dest) |
206 | 217 | // Will print |
207 | 218 | // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } |
208 | 219 | } |
209 | 220 | ``` |
210 | 221 | |
211 | ||
212 | 222 | ## Contact me |
213 | 223 | |
214 | 224 | If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario) |
217 | 227 | |
218 | 228 | Written by [Dario Castañé](http://dario.im). |
219 | 229 | |
220 | ## Top Contributors | |
221 | ||
222 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/0) | |
223 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/1) | |
224 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/2) | |
225 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/3) | |
226 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/4) | |
227 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/5) | |
228 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/6) | |
229 | [](https://sourcerer.io/fame/imdario/imdario/mergo/links/7) | |
230 | ||
231 | ||
232 | 230 | ## License |
233 | 231 | |
234 | 232 | [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE). |
0 | golang-github-imdario-mergo (0.3.12+git20210923.1.fd3dfc9-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream snapshot. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Sat, 25 Sep 2021 04:58:53 -0000 | |
5 | ||
0 | 6 | golang-github-imdario-mergo (0.3.8-3) unstable; urgency=medium |
1 | 7 | |
2 | 8 | [ Debian Janitor ] |
3 | 3 | // license that can be found in the LICENSE file. |
4 | 4 | |
5 | 5 | /* |
6 | Package mergo merges same-type structs and maps by setting default values in zero-value fields. | |
6 | A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. | |
7 | 7 | |
8 | Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). | |
8 | Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). | |
9 | ||
10 | Status | |
11 | ||
12 | It is ready for production use. It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc. | |
13 | ||
14 | Important note | |
15 | ||
16 | Please keep in mind that a problematic PR broke 0.3.9. We reverted it in 0.3.10. We consider 0.3.10 as stable but not bug-free. . Also, this version adds suppot for go modules. | |
17 | ||
18 | Keep in mind that in 0.3.2, Mergo changed Merge() and Map() signatures to support transformers. We added an optional/variadic argument so that it won't break the existing code. | |
19 | ||
20 | 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 in existing projects after the change (release 0.2.0). | |
21 | ||
22 | Install | |
23 | ||
24 | Do your usual installation procedure: | |
25 | ||
26 | go get github.com/imdario/mergo | |
27 | ||
28 | // use in your .go code | |
29 | import ( | |
30 | "github.com/imdario/mergo" | |
31 | ) | |
9 | 32 | |
10 | 33 | Usage |
11 | 34 | |
12 | From my own work-in-progress project: | |
35 | You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as they are zero values too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). | |
13 | 36 | |
14 | type networkConfig struct { | |
15 | Protocol string | |
16 | Address string | |
17 | ServerType string `json: "server_type"` | |
18 | Port uint16 | |
37 | if err := mergo.Merge(&dst, src); err != nil { | |
38 | // ... | |
19 | 39 | } |
20 | 40 | |
21 | type FssnConfig struct { | |
22 | Network networkConfig | |
41 | Also, you can merge overwriting values using the transformer WithOverride. | |
42 | ||
43 | if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { | |
44 | // ... | |
23 | 45 | } |
24 | 46 | |
25 | var fssnDefault = FssnConfig { | |
26 | networkConfig { | |
27 | "tcp", | |
28 | "127.0.0.1", | |
29 | "http", | |
30 | 31560, | |
31 | }, | |
47 | Additionally, you can map a map[string]interface{} to a struct (and otherwise, from struct to map), following the same restrictions as in Merge(). Keys are capitalized to find each corresponding exported field. | |
48 | ||
49 | if err := mergo.Map(&dst, srcMap); err != nil { | |
50 | // ... | |
32 | 51 | } |
33 | 52 | |
34 | // Inside a function [...] | |
53 | Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as map[string]interface{}. They will be just assigned as values. | |
35 | 54 | |
36 | if err := mergo.Merge(&config, fssnDefault); err != nil { | |
37 | log.Fatal(err) | |
55 | Here is a nice example: | |
56 | ||
57 | package main | |
58 | ||
59 | import ( | |
60 | "fmt" | |
61 | "github.com/imdario/mergo" | |
62 | ) | |
63 | ||
64 | type Foo struct { | |
65 | A string | |
66 | B int64 | |
38 | 67 | } |
39 | 68 | |
40 | // More code [...] | |
69 | func main() { | |
70 | src := Foo{ | |
71 | A: "one", | |
72 | B: 2, | |
73 | } | |
74 | dest := Foo{ | |
75 | A: "two", | |
76 | } | |
77 | mergo.Merge(&dest, src) | |
78 | fmt.Println(dest) | |
79 | // Will print | |
80 | // {two 2} | |
81 | } | |
82 | ||
83 | Transformers | |
84 | ||
85 | Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, time.Time is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero time.Time? | |
86 | ||
87 | package main | |
88 | ||
89 | import ( | |
90 | "fmt" | |
91 | "github.com/imdario/mergo" | |
92 | "reflect" | |
93 | "time" | |
94 | ) | |
95 | ||
96 | type timeTransformer struct { | |
97 | } | |
98 | ||
99 | func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { | |
100 | if typ == reflect.TypeOf(time.Time{}) { | |
101 | return func(dst, src reflect.Value) error { | |
102 | if dst.CanSet() { | |
103 | isZero := dst.MethodByName("IsZero") | |
104 | result := isZero.Call([]reflect.Value{}) | |
105 | if result[0].Bool() { | |
106 | dst.Set(src) | |
107 | } | |
108 | } | |
109 | return nil | |
110 | } | |
111 | } | |
112 | return nil | |
113 | } | |
114 | ||
115 | type Snapshot struct { | |
116 | Time time.Time | |
117 | // ... | |
118 | } | |
119 | ||
120 | func main() { | |
121 | src := Snapshot{time.Now()} | |
122 | dest := Snapshot{} | |
123 | mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{})) | |
124 | fmt.Println(dest) | |
125 | // Will print | |
126 | // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } | |
127 | } | |
128 | ||
129 | Contact me | |
130 | ||
131 | If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): https://twitter.com/im_dario | |
132 | ||
133 | About | |
134 | ||
135 | Written by Dario Castañé: https://da.rio.hn | |
136 | ||
137 | License | |
138 | ||
139 | BSD 3-Clause license, as Go language. | |
41 | 140 | |
42 | 141 | */ |
43 | 142 | package mergo |
0 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | |
1 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |
2 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= | |
3 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | type issue100s struct { | |
9 | Member interface{} | |
10 | } | |
11 | ||
12 | func TestIssue100(t *testing.T) { | |
13 | m := make(map[string]interface{}) | |
14 | m["Member"] = "anything" | |
15 | ||
16 | st := &issue100s{} | |
17 | if err := mergo.Map(st, m); err != nil { | |
18 | t.Error(err) | |
19 | } | |
20 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "reflect" | |
4 | "testing" | |
5 | ||
6 | "github.com/imdario/mergo" | |
7 | ) | |
8 | ||
9 | type Record struct { | |
10 | Data map[string]interface{} | |
11 | Mapping map[string]string | |
12 | } | |
13 | ||
14 | func StructToRecord(in interface{}) *Record { | |
15 | rec := Record{} | |
16 | rec.Data = make(map[string]interface{}) | |
17 | rec.Mapping = make(map[string]string) | |
18 | typ := reflect.TypeOf(in) | |
19 | for i := 0; i < typ.NumField(); i++ { | |
20 | field := typ.Field(i) | |
21 | dbFieldName := field.Tag.Get("db") | |
22 | if dbFieldName != "" { | |
23 | rec.Mapping[field.Name] = dbFieldName | |
24 | } | |
25 | } | |
26 | ||
27 | if err := mergo.Map(&rec.Data, in); err != nil { | |
28 | panic(err) | |
29 | } | |
30 | return &rec | |
31 | } | |
32 | ||
33 | func TestStructToRecord(t *testing.T) { | |
34 | type A struct { | |
35 | Name string `json:"name" db:"name"` | |
36 | CIDR string `json:"cidr" db:"cidr"` | |
37 | } | |
38 | type Record struct { | |
39 | Data map[string]interface{} | |
40 | Mapping map[string]string | |
41 | } | |
42 | a := A{Name: "David", CIDR: "10.0.0.0/8"} | |
43 | rec := StructToRecord(a) | |
44 | if len(rec.Mapping) < 2 { | |
45 | t.Fatalf("struct to record failed, no mapping, struct missing tags?, rec: %+v, a: %+v ", rec, a) | |
46 | } | |
47 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | func TestIssue121WithSliceDeepCopy(t *testing.T) { | |
9 | dst := map[string]interface{}{ | |
10 | "inter": map[string]interface{}{ | |
11 | "a": "1", | |
12 | "b": "2", | |
13 | }, | |
14 | } | |
15 | ||
16 | src := map[string]interface{}{ | |
17 | "inter": map[string]interface{}{ | |
18 | "a": "3", | |
19 | "c": "4", | |
20 | }, | |
21 | } | |
22 | ||
23 | if err := mergo.Merge(&dst, src, mergo.WithSliceDeepCopy); err != nil { | |
24 | t.Errorf("Error during the merge: %v", err) | |
25 | } | |
26 | ||
27 | if dst["inter"].(map[string]interface{})["a"].(string) != "3" { | |
28 | t.Error("inter.a should equal '3'") | |
29 | } | |
30 | ||
31 | if dst["inter"].(map[string]interface{})["c"].(string) != "4" { | |
32 | t.Error("inter.c should equal '4'") | |
33 | } | |
34 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | func TestIssue123(t *testing.T) { | |
9 | src := map[string]interface{}{ | |
10 | "col1": nil, | |
11 | "col2": 4, | |
12 | "col3": nil, | |
13 | } | |
14 | dst := map[string]interface{}{ | |
15 | "col1": 2, | |
16 | "col2": 3, | |
17 | "col3": 3, | |
18 | } | |
19 | ||
20 | // Expected behavior | |
21 | if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { | |
22 | t.Fatal(err) | |
23 | } | |
24 | testCases := []struct { | |
25 | key string | |
26 | expected interface{} | |
27 | }{ | |
28 | { | |
29 | "col1", | |
30 | nil, | |
31 | }, | |
32 | { | |
33 | "col2", | |
34 | 4, | |
35 | }, | |
36 | { | |
37 | "col3", | |
38 | nil, | |
39 | }, | |
40 | } | |
41 | for _, tC := range testCases { | |
42 | if dst[tC.key] != tC.expected { | |
43 | t.Fatalf("expected %v in dst[%q], got %v", tC.expected, tC.key, dst[tC.key]) | |
44 | } | |
45 | } | |
46 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "encoding/json" |
4 | 4 | "testing" |
5 | ) | |
6 | 5 | |
7 | var ( | |
8 | data = `{"FirstSlice":[], "SecondSlice": null}` | |
6 | "github.com/imdario/mergo" | |
9 | 7 | ) |
10 | 8 | |
11 | 9 | type settings struct { |
14 | 12 | } |
15 | 13 | |
16 | 14 | func TestIssue125MergeWithOverwrite(t *testing.T) { |
15 | var ( | |
16 | defaultSettings = settings{ | |
17 | FirstSlice: []string{}, | |
18 | SecondSlice: []string{}, | |
19 | } | |
20 | something settings | |
21 | data = `{"FirstSlice":[], "SecondSlice": null}` | |
22 | ) | |
17 | 23 | |
18 | defaultSettings := settings{ | |
19 | FirstSlice: []string{}, | |
20 | SecondSlice: []string{}, | |
21 | } | |
22 | ||
23 | var something settings | |
24 | 24 | if err := json.Unmarshal([]byte(data), &something); err != nil { |
25 | 25 | t.Errorf("Error while Unmarshalling maprequest: %s", err) |
26 | 26 | } |
27 | if err := Merge(&something, defaultSettings, WithOverrideEmptySlice); err != nil { | |
27 | ||
28 | if err := mergo.Merge(&something, defaultSettings, mergo.WithOverrideEmptySlice); err != nil { | |
28 | 29 | t.Errorf("Error while merging: %s", err) |
29 | 30 | } |
31 | ||
30 | 32 | if something.FirstSlice == nil { |
31 | 33 | t.Error("Invalid merging first slice") |
32 | 34 | } |
35 | ||
33 | 36 | if something.SecondSlice == nil { |
34 | 37 | t.Error("Invalid merging second slice") |
35 | 38 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | func TestIssue129Boolean(t *testing.T) { | |
9 | type Foo struct { | |
10 | A bool | |
11 | B bool | |
12 | } | |
13 | ||
14 | src := Foo{ | |
15 | A: true, | |
16 | B: false, | |
17 | } | |
18 | dst := Foo{ | |
19 | A: false, | |
20 | B: true, | |
21 | } | |
22 | ||
23 | // Standard behavior | |
24 | if err := mergo.Merge(&dst, src); err != nil { | |
25 | t.Error(err) | |
26 | } | |
27 | if dst.A != true { | |
28 | t.Errorf("expected true, got false") | |
29 | } | |
30 | if dst.B != true { | |
31 | t.Errorf("expected true, got false") | |
32 | } | |
33 | ||
34 | // Expected behavior | |
35 | dst = Foo{ | |
36 | A: false, | |
37 | B: true, | |
38 | } | |
39 | if err := mergo.Merge(&dst, src, mergo.WithOverwriteWithEmptyValue); err != nil { | |
40 | t.Error(err) | |
41 | } | |
42 | if dst.A != true { | |
43 | t.Errorf("expected true, got false") | |
44 | } | |
45 | if dst.B != false { | |
46 | t.Errorf("expected false, got true") | |
47 | } | |
48 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | type foz struct { | |
9 | A *bool | |
10 | B string | |
11 | } | |
12 | ||
13 | func TestIssue131MergeWithOverwriteWithEmptyValue(t *testing.T) { | |
14 | src := foz{ | |
15 | A: func(v bool) *bool { return &v }(false), | |
16 | B: "src", | |
17 | } | |
18 | dest := foz{ | |
19 | A: func(v bool) *bool { return &v }(true), | |
20 | B: "dest", | |
21 | } | |
22 | if err := mergo.Merge(&dest, src, mergo.WithOverwriteWithEmptyValue); err != nil { | |
23 | t.Error(err) | |
24 | } | |
25 | if *src.A != *dest.A { | |
26 | t.Errorf("dest.A not merged in properly: %v != %v", *src.A, *dest.A) | |
27 | } | |
28 | if src.B != dest.B { | |
29 | t.Errorf("dest.B not merged in properly: %v != %v", src.B, dest.B) | |
30 | } | |
31 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | type embeddedTestA struct { | |
9 | Name string | |
10 | Age uint8 | |
11 | } | |
12 | ||
13 | type embeddedTestB struct { | |
14 | embeddedTestA | |
15 | Address string | |
16 | } | |
17 | ||
18 | func TestMergeEmbedded(t *testing.T) { | |
19 | var ( | |
20 | err error | |
21 | a = &embeddedTestA{ | |
22 | "Suwon", 16, | |
23 | } | |
24 | b = &embeddedTestB{} | |
25 | ) | |
26 | ||
27 | if err := mergo.Merge(&b.embeddedTestA, *a); err != nil { | |
28 | t.Error(err) | |
29 | } | |
30 | ||
31 | if b.Name != "Suwon" { | |
32 | t.Errorf("%v %v", b.Name, err) | |
33 | } | |
34 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "encoding/json" | |
4 | "testing" | |
5 | ||
6 | "github.com/imdario/mergo" | |
7 | ) | |
8 | ||
9 | const issue138configuration string = ` | |
10 | { | |
11 | "Port": 80 | |
12 | } | |
13 | ` | |
14 | ||
15 | func TestIssue138(t *testing.T) { | |
16 | type config struct { | |
17 | Port uint16 | |
18 | } | |
19 | type compatibleConfig struct { | |
20 | Port float64 | |
21 | } | |
22 | ||
23 | foo := make(map[string]interface{}) | |
24 | // encoding/json unmarshals numbers as float64 | |
25 | // https://golang.org/pkg/encoding/json/#Unmarshal | |
26 | json.Unmarshal([]byte(issue138configuration), &foo) | |
27 | ||
28 | err := mergo.Map(&config{}, foo) | |
29 | if err == nil { | |
30 | t.Error("expected type mismatch error, got nil") | |
31 | } else { | |
32 | if err.Error() != "type mismatch on Port field: found float64, expected uint16" { | |
33 | t.Errorf("expected type mismatch error, got %q", err) | |
34 | } | |
35 | } | |
36 | ||
37 | c := compatibleConfig{} | |
38 | if err := mergo.Map(&c, foo); err != nil { | |
39 | t.Error(err) | |
40 | } | |
41 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "testing" | |
5 | ||
6 | "github.com/imdario/mergo" | |
7 | ) | |
8 | ||
9 | func TestIssue143(t *testing.T) { | |
10 | testCases := []struct { | |
11 | options []func(*mergo.Config) | |
12 | expected func(map[string]interface{}) error | |
13 | }{ | |
14 | { | |
15 | options: []func(*mergo.Config){mergo.WithOverride}, | |
16 | expected: func(m map[string]interface{}) error { | |
17 | properties := m["properties"].(map[string]interface{}) | |
18 | if properties["field1"] != "wrong" { | |
19 | return fmt.Errorf("expected %q, got %v", "wrong", properties["field1"]) | |
20 | } | |
21 | return nil | |
22 | }, | |
23 | }, | |
24 | { | |
25 | options: []func(*mergo.Config){}, | |
26 | expected: func(m map[string]interface{}) error { | |
27 | properties := m["properties"].(map[string]interface{}) | |
28 | if properties["field1"] == "wrong" { | |
29 | return fmt.Errorf("expected a map, got %v", "wrong") | |
30 | } | |
31 | return nil | |
32 | }, | |
33 | }, | |
34 | } | |
35 | for _, tC := range testCases { | |
36 | base := map[string]interface{}{ | |
37 | "properties": map[string]interface{}{ | |
38 | "field1": map[string]interface{}{ | |
39 | "type": "text", | |
40 | }, | |
41 | }, | |
42 | } | |
43 | ||
44 | err := mergo.Map( | |
45 | &base, | |
46 | map[string]interface{}{ | |
47 | "properties": map[string]interface{}{ | |
48 | "field1": "wrong", | |
49 | }, | |
50 | }, | |
51 | tC.options..., | |
52 | ) | |
53 | if err != nil { | |
54 | t.Error(err) | |
55 | } | |
56 | if err := tC.expected(base); err != nil { | |
57 | t.Error(err) | |
58 | } | |
59 | } | |
60 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | type user struct { | |
9 | Name string | |
10 | } | |
11 | ||
12 | type token struct { | |
13 | User *user | |
14 | Token *string | |
15 | } | |
16 | ||
17 | func TestIssue149(t *testing.T) { | |
18 | dest := &token{ | |
19 | User: &user{ | |
20 | Name: "destination", | |
21 | }, | |
22 | Token: nil, | |
23 | } | |
24 | tokenValue := "Issue149" | |
25 | src := &token{ | |
26 | User: nil, | |
27 | Token: &tokenValue, | |
28 | } | |
29 | if err := mergo.Merge(dest, src, mergo.WithOverwriteWithEmptyValue); err != nil { | |
30 | t.Error(err) | |
31 | } | |
32 | if dest.User != nil { | |
33 | t.Errorf("expected nil User, got %q", dest.User) | |
34 | } | |
35 | if dest.Token == nil { | |
36 | t.Errorf("expected not nil Token, got %q", *dest.Token) | |
37 | } | |
38 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | type structWithBlankField struct { | |
9 | _ struct{} | |
10 | A struct{} | |
11 | } | |
12 | ||
13 | func TestIssue174(t *testing.T) { | |
14 | dst := structWithBlankField{} | |
15 | src := structWithBlankField{} | |
16 | ||
17 | if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { | |
18 | t.Error(err) | |
19 | } | |
20 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "encoding/json" |
4 | 4 | "testing" |
5 | ) | |
6 | 5 | |
7 | var ( | |
8 | request = `{"timestamp":null, "name": "foo"}` | |
9 | maprequest = map[string]interface{}{ | |
10 | "timestamp": nil, | |
11 | "name": "foo", | |
12 | "newStuff": "foo", | |
13 | } | |
6 | "github.com/imdario/mergo" | |
14 | 7 | ) |
15 | 8 | |
16 | 9 | func TestIssue17MergeWithOverwrite(t *testing.T) { |
10 | var ( | |
11 | request = `{"timestamp":null, "name": "foo"}` | |
12 | maprequest = map[string]interface{}{ | |
13 | "timestamp": nil, | |
14 | "name": "foo", | |
15 | "newStuff": "foo", | |
16 | } | |
17 | ) | |
18 | ||
17 | 19 | var something map[string]interface{} |
18 | 20 | if err := json.Unmarshal([]byte(request), &something); err != nil { |
19 | 21 | t.Errorf("Error while Unmarshalling maprequest: %s", err) |
20 | 22 | } |
21 | if err := MergeWithOverwrite(&something, maprequest); err != nil { | |
23 | ||
24 | if err := mergo.MergeWithOverwrite(&something, maprequest); err != nil { | |
22 | 25 | t.Errorf("Error while merging: %s", err) |
23 | 26 | } |
24 | 27 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | 4 | "time" |
5 | ||
6 | "github.com/imdario/mergo" | |
5 | 7 | ) |
6 | 8 | |
7 | 9 | type document struct { |
17 | 19 | src := document{ |
18 | 20 | &expected, |
19 | 21 | } |
20 | if err := MergeWithOverwrite(&dst, src); err != nil { | |
22 | ||
23 | if err := mergo.MergeWithOverwrite(&dst, src); err != nil { | |
21 | 24 | t.Errorf("Error while merging %s", err) |
22 | 25 | } |
26 | ||
23 | 27 | if !dst.Created.Equal(*src.Created) { //--> https://golang.org/pkg/time/#pkg-overview |
24 | t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created) | |
28 | t.Errorf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created) | |
25 | 29 | } |
26 | 30 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | ||
5 | "github.com/imdario/mergo" | |
4 | 6 | ) |
5 | 7 | |
6 | 8 | type Foo struct { |
14 | 16 | Str: "b", |
15 | 17 | Bslice: []byte{1, 2}, |
16 | 18 | } |
17 | if err := Merge(&dest, toMerge); err != nil { | |
19 | ||
20 | if err := mergo.Merge(&dest, toMerge); err != nil { | |
18 | 21 | t.Errorf("Error while merging: %s", err) |
19 | 22 | } |
20 | 23 | // Merge doesn't overwrite an attribute if in destination it doesn't have a zero value. |
23 | 26 | t.Errorf("dest.Str should have not been override as it has a non-zero value: dest.Str(%v) != 'a'", dest.Str) |
24 | 27 | } |
25 | 28 | // If we want to override, we must use MergeWithOverwrite or Merge using WithOverride. |
26 | if err := Merge(&dest, toMerge, WithOverride); err != nil { | |
29 | if err := mergo.Merge(&dest, toMerge, mergo.WithOverride); err != nil { | |
27 | 30 | t.Errorf("Error while merging: %s", err) |
28 | 31 | } |
32 | ||
29 | 33 | if dest.Str != toMerge.Str { |
30 | 34 | t.Errorf("dest.Str should have been override: dest.Str(%v) != toMerge.Str(%v)", dest.Str, toMerge.Str) |
31 | 35 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | 4 | "time" |
5 | ||
6 | "github.com/imdario/mergo" | |
5 | 7 | ) |
6 | 8 | |
7 | 9 | type structWithoutTimePointer struct { |
17 | 19 | src := structWithoutTimePointer{ |
18 | 20 | expected, |
19 | 21 | } |
20 | if err := Merge(&dst, src); err != nil { | |
22 | ||
23 | if err := mergo.Merge(&dst, src); err != nil { | |
21 | 24 | t.Errorf("Error while merging %s", err) |
22 | 25 | } |
26 | ||
23 | 27 | if dst.Created == src.Created { |
24 | t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created) | |
28 | t.Errorf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created) | |
25 | 29 | } |
26 | 30 | } |
27 | 31 | |
32 | 36 | src := structWithoutTimePointer{ |
33 | 37 | expected, |
34 | 38 | } |
35 | if err := Merge(&dst, src); err != nil { | |
39 | ||
40 | if err := mergo.Merge(&dst, src); err != nil { | |
36 | 41 | t.Errorf("Error while merging %s", err) |
37 | 42 | } |
43 | ||
38 | 44 | if dst.Created == src.Created { |
39 | t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created) | |
45 | t.Errorf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created) | |
40 | 46 | } |
41 | 47 | } |
42 | 48 | |
49 | 55 | src := structWithoutTimePointer{ |
50 | 56 | expected, |
51 | 57 | } |
52 | if err := MergeWithOverwrite(&dst, src); err != nil { | |
58 | ||
59 | if err := mergo.MergeWithOverwrite(&dst, src); err != nil { | |
53 | 60 | t.Errorf("Error while merging %s", err) |
54 | 61 | } |
62 | ||
55 | 63 | if dst.Created != src.Created { |
56 | t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created) | |
64 | t.Errorf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created) | |
57 | 65 | } |
58 | 66 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | 4 | "time" |
5 | ||
6 | "github.com/imdario/mergo" | |
5 | 7 | ) |
6 | 8 | |
7 | 9 | type testStruct struct { |
11 | 13 | func TestIssue50Merge(t *testing.T) { |
12 | 14 | to := testStruct{} |
13 | 15 | from := testStruct{} |
14 | if err := Merge(&to, from); err != nil { | |
16 | ||
17 | if err := mergo.Merge(&to, from); err != nil { | |
15 | 18 | t.Fail() |
16 | 19 | } |
17 | 20 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "reflect" |
4 | 4 | "testing" |
5 | 5 | "time" |
6 | ||
7 | "github.com/imdario/mergo" | |
6 | 8 | ) |
7 | 9 | |
8 | 10 | type structWithTime struct { |
19 | 21 | if dst.CanSet() { |
20 | 22 | if t.overwrite { |
21 | 23 | isZero := src.MethodByName("IsZero") |
24 | ||
22 | 25 | result := isZero.Call([]reflect.Value{}) |
23 | 26 | if !result[0].Bool() { |
24 | 27 | dst.Set(src) |
25 | 28 | } |
26 | 29 | } else { |
27 | 30 | isZero := dst.MethodByName("IsZero") |
31 | ||
28 | 32 | result := isZero.Call([]reflect.Value{}) |
29 | 33 | if result[0].Bool() { |
30 | 34 | dst.Set(src) |
41 | 45 | now := time.Now() |
42 | 46 | dst := structWithTime{now} |
43 | 47 | src := structWithTime{} |
44 | if err := MergeWithOverwrite(&dst, src); err != nil { | |
48 | ||
49 | if err := mergo.MergeWithOverwrite(&dst, src); err != nil { | |
45 | 50 | t.FailNow() |
46 | 51 | } |
52 | ||
47 | 53 | if !dst.Birth.IsZero() { |
48 | t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) | |
54 | t.Errorf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) | |
49 | 55 | } |
50 | 56 | } |
51 | 57 | |
53 | 59 | now := time.Now() |
54 | 60 | dst := structWithTime{now} |
55 | 61 | src := structWithTime{} |
56 | if err := MergeWithOverwrite(&dst, src, WithTransformers(timeTransfomer{true})); err != nil { | |
62 | ||
63 | if err := mergo.MergeWithOverwrite(&dst, src, mergo.WithTransformers(timeTransfomer{true})); err != nil { | |
57 | 64 | t.FailNow() |
58 | 65 | } |
66 | ||
59 | 67 | if dst.Birth.IsZero() { |
60 | t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) | |
68 | t.Errorf("dst should not have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) | |
61 | 69 | } |
62 | 70 | } |
63 | 71 | |
65 | 73 | now := time.Now() |
66 | 74 | dst := structWithTime{} |
67 | 75 | src := structWithTime{now} |
68 | if err := MergeWithOverwrite(&dst, src); err != nil { | |
76 | ||
77 | if err := mergo.MergeWithOverwrite(&dst, src); err != nil { | |
69 | 78 | t.FailNow() |
70 | 79 | } |
80 | ||
71 | 81 | if dst.Birth.IsZero() { |
72 | t.Fatalf("dst should have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{}) | |
82 | t.Errorf("dst should have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{}) | |
73 | 83 | } |
74 | 84 | } |
75 | 85 | |
77 | 87 | now := time.Now() |
78 | 88 | dst := structWithTime{} |
79 | 89 | src := structWithTime{now} |
80 | if err := Merge(&dst, src); err != nil { | |
90 | ||
91 | if err := mergo.Merge(&dst, src); err != nil { | |
81 | 92 | t.FailNow() |
82 | 93 | } |
94 | ||
83 | 95 | if !dst.Birth.IsZero() { |
84 | t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{}) | |
96 | t.Errorf("dst should not have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{}) | |
85 | 97 | } |
86 | 98 | } |
87 | 99 | |
89 | 101 | now := time.Now() |
90 | 102 | dst := structWithTime{} |
91 | 103 | src := structWithTime{now} |
92 | if err := Merge(&dst, src, WithTransformers(timeTransfomer{})); err != nil { | |
104 | ||
105 | if err := mergo.Merge(&dst, src, mergo.WithTransformers(timeTransfomer{})); err != nil { | |
93 | 106 | t.FailNow() |
94 | 107 | } |
108 | ||
95 | 109 | if dst.Birth.IsZero() { |
96 | t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) | |
110 | t.Errorf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now) | |
97 | 111 | } |
98 | 112 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "reflect" |
4 | 4 | "testing" |
5 | ||
6 | "github.com/imdario/mergo" | |
5 | 7 | ) |
6 | 8 | |
7 | 9 | func TestIssue61MergeNilMap(t *testing.T) { |
10 | 12 | } |
11 | 13 | t1 := T{} |
12 | 14 | t2 := T{I: map[string][]string{"hi": {"there"}}} |
13 | if err := Merge(&t1, t2); err != nil { | |
15 | ||
16 | if err := mergo.Merge(&t1, t2); err != nil { | |
14 | 17 | t.Fail() |
15 | 18 | } |
19 | ||
16 | 20 | if !reflect.DeepEqual(t2, T{I: map[string][]string{"hi": {"there"}}}) { |
17 | 21 | t.FailNow() |
18 | 22 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | ||
5 | "github.com/imdario/mergo" | |
4 | 6 | ) |
5 | 7 | |
6 | 8 | type Student struct { |
8 | 10 | Books []string |
9 | 11 | } |
10 | 12 | |
11 | var testData = []struct { | |
13 | type issue64TestData struct { | |
12 | 14 | S1 Student |
13 | 15 | S2 Student |
14 | 16 | ExpectedSlice []string |
15 | }{ | |
16 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"a", "B"}}, | |
17 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}}, | |
18 | {Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}}, | |
19 | {Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}}, | |
17 | } | |
18 | ||
19 | func issue64Data() []issue64TestData { | |
20 | return []issue64TestData{ | |
21 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"a", "B"}}, | |
22 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}}, | |
23 | {Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}}, | |
24 | {Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}}, | |
25 | } | |
20 | 26 | } |
21 | 27 | |
22 | 28 | func TestIssue64MergeSliceWithOverride(t *testing.T) { |
23 | for _, data := range testData { | |
24 | err := Merge(&data.S2, data.S1, WithOverride) | |
29 | for _, data := range issue64Data() { | |
30 | err := mergo.Merge(&data.S2, data.S1, mergo.WithOverride) | |
25 | 31 | if err != nil { |
26 | 32 | t.Errorf("Error while merging %s", err) |
27 | 33 | } |
34 | ||
28 | 35 | if len(data.S2.Books) != len(data.ExpectedSlice) { |
29 | t.Fatalf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice)) | |
36 | t.Errorf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice)) | |
30 | 37 | } |
38 | ||
31 | 39 | for i, val := range data.S2.Books { |
32 | 40 | if val != data.ExpectedSlice[i] { |
33 | t.Fatalf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val) | |
41 | t.Errorf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val) | |
34 | 42 | } |
35 | 43 | } |
36 | 44 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | ||
5 | "github.com/imdario/mergo" | |
4 | 6 | ) |
5 | 7 | |
6 | 8 | type PrivateSliceTest66 struct { |
16 | 18 | p2 := PrivateSliceTest66{ |
17 | 19 | PublicStrings: []string{"six", "seven"}, |
18 | 20 | } |
19 | if err := Merge(&p1, p2); err != nil { | |
20 | t.Fatalf("Error during the merge: %v", err) | |
21 | ||
22 | if err := mergo.Merge(&p1, p2); err != nil { | |
23 | t.Errorf("Error during the merge: %v", err) | |
21 | 24 | } |
25 | ||
22 | 26 | if len(p1.PublicStrings) != 3 { |
23 | t.Error("5 elements should be in 'PublicStrings' field") | |
27 | t.Error("3 elements should be in 'PublicStrings' field, when no append") | |
24 | 28 | } |
29 | ||
25 | 30 | if len(p1.privateStrings) != 2 { |
26 | 31 | t.Error("2 elements should be in 'privateStrings' field") |
27 | 32 | } |
35 | 40 | p2 := PrivateSliceTest66{ |
36 | 41 | PublicStrings: []string{"six", "seven"}, |
37 | 42 | } |
38 | if err := Merge(&p1, p2, WithAppendSlice); err != nil { | |
39 | t.Fatalf("Error during the merge: %v", err) | |
43 | ||
44 | if err := mergo.Merge(&p1, p2, mergo.WithAppendSlice); err != nil { | |
45 | t.Errorf("Error during the merge: %v", err) | |
40 | 46 | } |
47 | ||
41 | 48 | if len(p1.PublicStrings) != 5 { |
42 | 49 | t.Error("5 elements should be in 'PublicStrings' field") |
43 | 50 | } |
51 | ||
44 | 52 | if len(p1.privateStrings) != 2 { |
45 | 53 | t.Error("2 elements should be in 'privateStrings' field") |
46 | 54 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | type issue83My struct { | |
9 | Data []int | |
10 | } | |
11 | ||
12 | func TestIssue83(t *testing.T) { | |
13 | dst := issue83My{Data: []int{1, 2, 3}} | |
14 | new := issue83My{} | |
15 | if err := mergo.Merge(&dst, new, mergo.WithOverwriteWithEmptyValue); err != nil { | |
16 | t.Error(err) | |
17 | } | |
18 | if len(dst.Data) > 0 { | |
19 | t.Errorf("expected empty slice, got %v", dst.Data) | |
20 | } | |
21 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | ||
5 | "github.com/imdario/mergo" | |
4 | 6 | ) |
5 | 7 | |
6 | 8 | type DstStructIssue84 struct { |
26 | 28 | p2 := map[string]interface{}{ |
27 | 29 | "A": 3, "B": 4, "C": 0, |
28 | 30 | } |
29 | if err := Map(&p1, p2, WithOverride); err != nil { | |
30 | t.Fatalf("Error during the merge: %v", err) | |
31 | ||
32 | if err := mergo.Map(&p1, p2, mergo.WithOverride); err != nil { | |
33 | t.Errorf("Error during the merge: %v", err) | |
31 | 34 | } |
35 | ||
32 | 36 | if p1.C != 0 { |
33 | 37 | t.Error("C field should become '0'") |
34 | 38 | } |
41 | 45 | p2 := map[string]interface{}{ |
42 | 46 | "A": 3, "B": 4, |
43 | 47 | } |
44 | if err := Map(&p1, p2, WithOverride); err != nil { | |
45 | t.Fatalf("Error during the merge: %v", err) | |
48 | ||
49 | if err := mergo.Map(&p1, p2, mergo.WithOverride); err != nil { | |
50 | t.Errorf("Error during the merge: %v", err) | |
46 | 51 | } |
52 | ||
47 | 53 | if p1.C != 2 { |
48 | 54 | t.Error("C field should be '2'") |
49 | 55 | } |
64 | 70 | "A": 0, "B": 0, "C": 5, |
65 | 71 | }, "B": 4, "C": 0, |
66 | 72 | } |
67 | if err := Map(&p1, p2, WithOverride); err != nil { | |
68 | t.Fatalf("Error during the merge: %v", err) | |
73 | ||
74 | if err := mergo.Map(&p1, p2, mergo.WithOverride); err != nil { | |
75 | t.Errorf("Error during the merge: %v", err) | |
69 | 76 | } |
77 | ||
70 | 78 | if p1.B != 4 { |
71 | 79 | t.Error("A.C field should become '4'") |
72 | 80 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | func TestIssue89Boolean(t *testing.T) { | |
9 | type Foo struct { | |
10 | Bar bool `json:"bar"` | |
11 | } | |
12 | ||
13 | src := Foo{Bar: true} | |
14 | dst := Foo{Bar: false} | |
15 | ||
16 | if err := mergo.Merge(&dst, src); err != nil { | |
17 | t.Error(err) | |
18 | } | |
19 | if dst.Bar == false { | |
20 | t.Errorf("expected true, got false") | |
21 | } | |
22 | } | |
23 | ||
24 | func TestIssue89MergeWithEmptyValue(t *testing.T) { | |
25 | p1 := map[string]interface{}{ | |
26 | "A": 3, "B": "note", "C": true, | |
27 | } | |
28 | p2 := map[string]interface{}{ | |
29 | "B": "", "C": false, | |
30 | } | |
31 | if err := mergo.Merge(&p1, p2, mergo.WithOverwriteWithEmptyValue); err != nil { | |
32 | t.Error(err) | |
33 | } | |
34 | testCases := []struct { | |
35 | key string | |
36 | expected interface{} | |
37 | }{ | |
38 | { | |
39 | "A", | |
40 | 3, | |
41 | }, | |
42 | { | |
43 | "B", | |
44 | "", | |
45 | }, | |
46 | { | |
47 | "C", | |
48 | false, | |
49 | }, | |
50 | } | |
51 | for _, tC := range testCases { | |
52 | if p1[tC.key] != tC.expected { | |
53 | t.Errorf("expected %v in p1[%q], got %v", tC.expected, tC.key, p1[tC.key]) | |
54 | } | |
55 | } | |
56 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "github.com/imdario/mergo" | |
4 | "reflect" | |
5 | "testing" | |
6 | ) | |
7 | ||
8 | type structWithStringMap struct { | |
9 | Data map[string]string | |
10 | } | |
11 | ||
12 | ||
13 | func TestIssue90(t *testing.T) { | |
14 | dst := map[string]structWithStringMap{ | |
15 | "struct": { | |
16 | Data: nil, | |
17 | }, | |
18 | } | |
19 | src := map[string]structWithStringMap{ | |
20 | "struct": { | |
21 | Data: map[string]string{ | |
22 | "foo": "bar", | |
23 | }, | |
24 | }, | |
25 | } | |
26 | expected := map[string]structWithStringMap{ | |
27 | "struct": { | |
28 | Data: map[string]string{ | |
29 | "foo": "bar", | |
30 | }, | |
31 | }, | |
32 | } | |
33 | ||
34 | err := mergo.Merge(&dst, src, mergo.WithOverride) | |
35 | if err != nil { | |
36 | t.Errorf("unexpected error %v", err) | |
37 | } | |
38 | ||
39 | if !reflect.DeepEqual(dst, expected) { | |
40 | t.Errorf("expected: %#v\ngot: %#v", expected, dst) | |
41 | } | |
42 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | var testDataS = []struct { | |
9 | S1 Student | |
10 | S2 Student | |
11 | ExpectedSlice []string | |
12 | }{ | |
13 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"1", "a", "B"}}, | |
14 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}}, | |
15 | {Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}}, | |
16 | {Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}}, | |
17 | } | |
18 | ||
19 | func TestMergeSliceWithOverrideWithAppendSlice(t *testing.T) { | |
20 | for _, data := range testDataS { | |
21 | err := mergo.Merge(&data.S2, data.S1, mergo.WithOverride, mergo.WithAppendSlice) | |
22 | if err != nil { | |
23 | t.Errorf("Error while merging %s", err) | |
24 | } | |
25 | ||
26 | if len(data.S2.Books) != len(data.ExpectedSlice) { | |
27 | t.Errorf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice)) | |
28 | } | |
29 | ||
30 | for i, val := range data.S2.Books { | |
31 | if val != data.ExpectedSlice[i] { | |
32 | t.Errorf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val) | |
33 | } | |
34 | } | |
35 | } | |
36 | } |
140 | 140 | } |
141 | 141 | |
142 | 142 | func _map(dst, src interface{}, opts ...func(*Config)) error { |
143 | if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { | |
144 | return ErrNonPointerAgument | |
145 | } | |
143 | 146 | var ( |
144 | 147 | vDst, vSrc reflect.Value |
145 | 148 | err error |
12 | 12 | "reflect" |
13 | 13 | ) |
14 | 14 | |
15 | func hasExportedField(dst reflect.Value) (exported bool) { | |
15 | func hasMergeableFields(dst reflect.Value) (exported bool) { | |
16 | 16 | for i, n := 0, dst.NumField(); i < n; i++ { |
17 | 17 | field := dst.Type().Field(i) |
18 | 18 | if field.Anonymous && dst.Field(i).Kind() == reflect.Struct { |
19 | exported = exported || hasExportedField(dst.Field(i)) | |
20 | } else { | |
19 | exported = exported || hasMergeableFields(dst.Field(i)) | |
20 | } else if isExportedComponent(&field) { | |
21 | 21 | exported = exported || len(field.PkgPath) == 0 |
22 | 22 | } |
23 | 23 | } |
24 | 24 | return |
25 | } | |
26 | ||
27 | func isExportedComponent(field *reflect.StructField) bool { | |
28 | pkgPath := field.PkgPath | |
29 | if len(pkgPath) > 0 { | |
30 | return false | |
31 | } | |
32 | c := field.Name[0] | |
33 | if 'a' <= c && c <= 'z' || c == '_' { | |
34 | return false | |
35 | } | |
36 | return true | |
25 | 37 | } |
26 | 38 | |
27 | 39 | type Config struct { |
31 | 43 | Transformers Transformers |
32 | 44 | overwriteWithEmptyValue bool |
33 | 45 | overwriteSliceWithEmptyValue bool |
46 | sliceDeepCopy bool | |
47 | debug bool | |
34 | 48 | } |
35 | 49 | |
36 | 50 | type Transformers interface { |
45 | 59 | typeCheck := config.TypeCheck |
46 | 60 | overwriteWithEmptySrc := config.overwriteWithEmptyValue |
47 | 61 | overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue |
48 | config.overwriteWithEmptyValue = false | |
62 | sliceDeepCopy := config.sliceDeepCopy | |
49 | 63 | |
50 | 64 | if !src.IsValid() { |
51 | 65 | return |
73 | 87 | |
74 | 88 | switch dst.Kind() { |
75 | 89 | case reflect.Struct: |
76 | if hasExportedField(dst) { | |
90 | if hasMergeableFields(dst) { | |
77 | 91 | for i, n := 0, dst.NumField(); i < n; i++ { |
78 | 92 | if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { |
79 | 93 | return |
80 | 94 | } |
81 | 95 | } |
82 | 96 | } else { |
83 | if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) { | |
97 | if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) { | |
84 | 98 | dst.Set(src) |
85 | 99 | } |
86 | 100 | } |
87 | 101 | case reflect.Map: |
88 | 102 | if dst.IsNil() && !src.IsNil() { |
89 | dst.Set(reflect.MakeMap(dst.Type())) | |
90 | } | |
103 | if dst.CanSet() { | |
104 | dst.Set(reflect.MakeMap(dst.Type())) | |
105 | } else { | |
106 | dst = src | |
107 | return | |
108 | } | |
109 | } | |
110 | ||
111 | if src.Kind() != reflect.Map { | |
112 | if overwrite { | |
113 | dst.Set(src) | |
114 | } | |
115 | return | |
116 | } | |
117 | ||
91 | 118 | for _, key := range src.MapKeys() { |
92 | 119 | srcElement := src.MapIndex(key) |
93 | 120 | if !srcElement.IsValid() { |
97 | 124 | switch srcElement.Kind() { |
98 | 125 | case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice: |
99 | 126 | if srcElement.IsNil() { |
127 | if overwrite { | |
128 | dst.SetMapIndex(key, srcElement) | |
129 | } | |
100 | 130 | continue |
101 | 131 | } |
102 | 132 | fallthrough |
131 | 161 | dstSlice = reflect.ValueOf(dstElement.Interface()) |
132 | 162 | } |
133 | 163 | |
134 | if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { | |
164 | if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { | |
135 | 165 | if typeCheck && srcSlice.Type() != dstSlice.Type() { |
136 | 166 | return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) |
137 | 167 | } |
141 | 171 | return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) |
142 | 172 | } |
143 | 173 | dstSlice = reflect.AppendSlice(dstSlice, srcSlice) |
174 | } else if sliceDeepCopy { | |
175 | i := 0 | |
176 | for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ { | |
177 | srcElement := srcSlice.Index(i) | |
178 | dstElement := dstSlice.Index(i) | |
179 | ||
180 | if srcElement.CanInterface() { | |
181 | srcElement = reflect.ValueOf(srcElement.Interface()) | |
182 | } | |
183 | if dstElement.CanInterface() { | |
184 | dstElement = reflect.ValueOf(dstElement.Interface()) | |
185 | } | |
186 | ||
187 | if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { | |
188 | return | |
189 | } | |
190 | } | |
191 | ||
144 | 192 | } |
145 | 193 | dst.SetMapIndex(key, dstSlice) |
146 | 194 | } |
160 | 208 | if !dst.CanSet() { |
161 | 209 | break |
162 | 210 | } |
163 | if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { | |
211 | if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { | |
164 | 212 | dst.Set(src) |
165 | 213 | } else if config.AppendSlice { |
166 | 214 | if src.Type() != dst.Type() { |
167 | 215 | return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type()) |
168 | 216 | } |
169 | 217 | dst.Set(reflect.AppendSlice(dst, src)) |
218 | } else if sliceDeepCopy { | |
219 | for i := 0; i < src.Len() && i < dst.Len(); i++ { | |
220 | srcElement := src.Index(i) | |
221 | dstElement := dst.Index(i) | |
222 | if srcElement.CanInterface() { | |
223 | srcElement = reflect.ValueOf(srcElement.Interface()) | |
224 | } | |
225 | if dstElement.CanInterface() { | |
226 | dstElement = reflect.ValueOf(dstElement.Interface()) | |
227 | } | |
228 | ||
229 | if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { | |
230 | return | |
231 | } | |
232 | } | |
170 | 233 | } |
171 | 234 | case reflect.Ptr: |
172 | 235 | fallthrough |
173 | 236 | case reflect.Interface: |
174 | if src.IsNil() { | |
175 | break | |
176 | } | |
177 | ||
178 | if dst.Kind() != reflect.Ptr && src.Type().AssignableTo(dst.Type()) { | |
179 | if dst.IsNil() || overwrite { | |
180 | if dst.CanSet() && (overwrite || isEmptyValue(dst)) { | |
181 | dst.Set(src) | |
182 | } | |
237 | if isReflectNil(src) { | |
238 | if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) { | |
239 | dst.Set(src) | |
183 | 240 | } |
184 | 241 | break |
185 | 242 | } |
202 | 259 | } |
203 | 260 | break |
204 | 261 | } |
262 | ||
205 | 263 | if dst.IsNil() || overwrite { |
206 | 264 | if dst.CanSet() && (overwrite || isEmptyValue(dst)) { |
207 | 265 | dst.Set(src) |
208 | 266 | } |
209 | } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { | |
210 | return | |
267 | break | |
268 | } | |
269 | ||
270 | if dst.Elem().Kind() == src.Elem().Kind() { | |
271 | if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { | |
272 | return | |
273 | } | |
274 | break | |
211 | 275 | } |
212 | 276 | default: |
213 | if dst.CanSet() && (!isEmptyValue(src) || overwriteWithEmptySrc) && (overwrite || isEmptyValue(dst)) { | |
214 | dst.Set(src) | |
277 | mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) | |
278 | if mustSet { | |
279 | if dst.CanSet() { | |
280 | dst.Set(src) | |
281 | } else { | |
282 | dst = src | |
283 | } | |
215 | 284 | } |
216 | 285 | } |
217 | 286 | |
245 | 314 | config.Overwrite = true |
246 | 315 | } |
247 | 316 | |
248 | // WithOverride will make merge override empty dst slice with empty src slice. | |
317 | // WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values. | |
318 | func WithOverwriteWithEmptyValue(config *Config) { | |
319 | config.Overwrite = true | |
320 | config.overwriteWithEmptyValue = true | |
321 | } | |
322 | ||
323 | // WithOverrideEmptySlice will make merge override empty dst slice with empty src slice. | |
249 | 324 | func WithOverrideEmptySlice(config *Config) { |
250 | 325 | config.overwriteSliceWithEmptyValue = true |
251 | 326 | } |
260 | 335 | config.TypeCheck = true |
261 | 336 | } |
262 | 337 | |
338 | // WithSliceDeepCopy will merge slice element one by one with Overwrite flag. | |
339 | func WithSliceDeepCopy(config *Config) { | |
340 | config.sliceDeepCopy = true | |
341 | config.Overwrite = true | |
342 | } | |
343 | ||
263 | 344 | func merge(dst, src interface{}, opts ...func(*Config)) error { |
345 | if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { | |
346 | return ErrNonPointerAgument | |
347 | } | |
264 | 348 | var ( |
265 | 349 | vDst, vSrc reflect.Value |
266 | 350 | err error |
280 | 364 | } |
281 | 365 | return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) |
282 | 366 | } |
367 | ||
368 | // IsReflectNil is the reflect value provided nil | |
369 | func isReflectNil(v reflect.Value) bool { | |
370 | k := v.Kind() | |
371 | switch k { | |
372 | case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr: | |
373 | // Both interface and slice are nil if first word is 0. | |
374 | // Both are always bigger than a word; assume flagIndir. | |
375 | return v.IsNil() | |
376 | default: | |
377 | return false | |
378 | } | |
379 | } |
0 | package mergo | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ) | |
5 | ||
6 | var testDataS = []struct { | |
7 | S1 Student | |
8 | S2 Student | |
9 | ExpectedSlice []string | |
10 | }{ | |
11 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"1", "a", "B"}}, | |
12 | {Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}}, | |
13 | {Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}}, | |
14 | {Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}}, | |
15 | } | |
16 | ||
17 | func TestMergeSliceWithOverrideWithAppendSlice(t *testing.T) { | |
18 | for _, data := range testDataS { | |
19 | err := Merge(&data.S2, data.S1, WithOverride, WithAppendSlice) | |
20 | if err != nil { | |
21 | t.Errorf("Error while merging %s", err) | |
22 | } | |
23 | if len(data.S2.Books) != len(data.ExpectedSlice) { | |
24 | t.Fatalf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice)) | |
25 | } | |
26 | for i, val := range data.S2.Books { | |
27 | if val != data.ExpectedSlice[i] { | |
28 | t.Fatalf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val) | |
29 | } | |
30 | } | |
31 | } | |
32 | } |
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 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "reflect" |
4 | 4 | "testing" |
5 | ||
6 | "github.com/imdario/mergo" | |
5 | 7 | ) |
6 | 8 | |
7 | 9 | type transformer struct { |
28 | 30 | func TestMergeWithTransformerNilStruct(t *testing.T) { |
29 | 31 | a := foo{s: "foo"} |
30 | 32 | b := foo{Bar: &bar{i: 2, s: map[string]string{"foo": "bar"}}} |
31 | if err := Merge(&a, &b, WithOverride, WithTransformers(&transformer{ | |
33 | ||
34 | if err := mergo.Merge(&a, &b, mergo.WithOverride, mergo.WithTransformers(&transformer{ | |
32 | 35 | m: map[reflect.Type]func(dst, src reflect.Value) error{ |
33 | 36 | reflect.TypeOf(&bar{}): func(dst, src reflect.Value) error { |
34 | 37 | // Do sthg with Elem |
38 | 41 | }, |
39 | 42 | }, |
40 | 43 | })); err != nil { |
41 | t.Fatal(err) | |
44 | t.Error(err) | |
42 | 45 | } |
46 | ||
43 | 47 | if a.s != "foo" { |
44 | t.Fatalf("b not merged in properly: a.s.Value(%s) != expected(%s)", a.s, "foo") | |
48 | t.Errorf("b not merged in properly: a.s.Value(%s) != expected(%s)", a.s, "foo") | |
45 | 49 | } |
50 | ||
46 | 51 | if a.Bar == nil { |
47 | t.Fatalf("b not merged in properly: a.Bar shouldn't be nil") | |
52 | t.Errorf("b not merged in properly: a.Bar shouldn't be nil") | |
48 | 53 | } |
49 | 54 | } |
55 | ||
56 | func TestMergeNonPointer(t *testing.T) { | |
57 | dst := bar{ | |
58 | i: 1, | |
59 | } | |
60 | src := bar{ | |
61 | i: 2, | |
62 | s: map[string]string{ | |
63 | "a": "1", | |
64 | }, | |
65 | } | |
66 | want := mergo.ErrNonPointerAgument | |
67 | ||
68 | if got := mergo.Merge(dst, src); got != want { | |
69 | t.Errorf("want: %s, got: %s", want, got) | |
70 | } | |
71 | } | |
72 | ||
73 | func TestMapNonPointer(t *testing.T) { | |
74 | dst := make(map[string]bar) | |
75 | src := map[string]bar{ | |
76 | "a": { | |
77 | i: 2, | |
78 | s: map[string]string{ | |
79 | "a": "1", | |
80 | }, | |
81 | }, | |
82 | } | |
83 | want := mergo.ErrNonPointerAgument | |
84 | if got := mergo.Merge(dst, src); got != want { | |
85 | t.Errorf("want: %s, got: %s", want, got) | |
86 | } | |
87 | } |
19 | 19 | ErrNotSupported = errors.New("only structs and maps are supported") |
20 | 20 | ErrExpectedMapAsDestination = errors.New("dst was expected to be a map") |
21 | 21 | ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct") |
22 | ErrNonPointerAgument = errors.New("dst must be a pointer") | |
22 | 23 | ) |
23 | 24 | |
24 | 25 | // During deepMerge, must keep track of checks that are |
74 | 75 | } |
75 | 76 | return |
76 | 77 | } |
77 | ||
78 | // Traverses recursively both values, assigning src's fields values to dst. | |
79 | // The map argument tracks comparisons that have already been seen, which allows | |
80 | // short circuiting on recursive types. | |
81 | func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) { | |
82 | if dst.CanAddr() { | |
83 | addr := dst.UnsafeAddr() | |
84 | h := 17 * addr | |
85 | seen := visited[h] | |
86 | typ := dst.Type() | |
87 | for p := seen; p != nil; p = p.next { | |
88 | if p.ptr == addr && p.typ == typ { | |
89 | return nil | |
90 | } | |
91 | } | |
92 | // Remember, remember... | |
93 | visited[h] = &visit{addr, typ, seen} | |
94 | } | |
95 | return // TODO refactor | |
96 | } |
2 | 2 | // Use of this source code is governed by a BSD-style |
3 | 3 | // license that can be found in the LICENSE file. |
4 | 4 | |
5 | package mergo | |
5 | package mergo_test | |
6 | 6 | |
7 | 7 | import ( |
8 | 8 | "io/ioutil" |
11 | 11 | "testing" |
12 | 12 | "time" |
13 | 13 | |
14 | "github.com/stretchr/testify/assert" | |
15 | ||
14 | "github.com/imdario/mergo" | |
16 | 15 | "gopkg.in/yaml.v2" |
17 | 16 | ) |
18 | 17 | |
76 | 75 | expected.Name = "B" |
77 | 76 | expected.KeyValue = ekv |
78 | 77 | |
79 | Merge(&b, a) | |
78 | if err := mergo.Merge(&b, a); err != nil { | |
79 | t.Error(err) | |
80 | } | |
80 | 81 | |
81 | 82 | if !reflect.DeepEqual(b, expected) { |
82 | 83 | t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected) |
84 | 85 | } |
85 | 86 | |
86 | 87 | func TestNil(t *testing.T) { |
87 | if err := Merge(nil, nil); err != ErrNilArguments { | |
88 | if err := mergo.Merge(nil, nil); err != mergo.ErrNilArguments { | |
88 | 89 | t.Fail() |
89 | 90 | } |
90 | 91 | } |
92 | 93 | func TestDifferentTypes(t *testing.T) { |
93 | 94 | a := simpleTest{42} |
94 | 95 | b := 42 |
95 | if err := Merge(&a, b); err != ErrDifferentArgumentsTypes { | |
96 | if err := mergo.Merge(&a, b); err != mergo.ErrDifferentArgumentsTypes { | |
96 | 97 | t.Fail() |
97 | 98 | } |
98 | 99 | } |
100 | 101 | func TestSimpleStruct(t *testing.T) { |
101 | 102 | a := simpleTest{} |
102 | 103 | b := simpleTest{42} |
103 | if err := Merge(&a, b); err != nil { | |
104 | if err := mergo.Merge(&a, b); err != nil { | |
104 | 105 | t.FailNow() |
105 | 106 | } |
106 | 107 | if a.Value != 42 { |
107 | t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value) | |
108 | t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value) | |
108 | 109 | } |
109 | 110 | if !reflect.DeepEqual(a, b) { |
110 | 111 | t.FailNow() |
115 | 116 | a := complexTest{} |
116 | 117 | a.ID = "athing" |
117 | 118 | b := complexTest{simpleTest{42}, 1, "bthing"} |
118 | if err := Merge(&a, b); err != nil { | |
119 | if err := mergo.Merge(&a, b); err != nil { | |
119 | 120 | t.FailNow() |
120 | 121 | } |
121 | 122 | if a.St.Value != 42 { |
122 | t.Fatalf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value) | |
123 | t.Errorf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value) | |
123 | 124 | } |
124 | 125 | if a.sz == 1 { |
125 | t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz) | |
126 | t.Errorf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz) | |
126 | 127 | } |
127 | 128 | if a.ID == b.ID { |
128 | t.Fatalf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID) | |
129 | t.Errorf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID) | |
129 | 130 | } |
130 | 131 | } |
131 | 132 | |
134 | 135 | b := complexTest{simpleTest{42}, 2, ""} |
135 | 136 | |
136 | 137 | expect := complexTest{simpleTest{42}, 1, "do-not-overwrite-with-empty-value"} |
137 | if err := MergeWithOverwrite(&a, b); err != nil { | |
138 | if err := mergo.MergeWithOverwrite(&a, b); err != nil { | |
138 | 139 | t.FailNow() |
139 | 140 | } |
140 | 141 | |
141 | 142 | if !reflect.DeepEqual(a, expect) { |
142 | t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", a, expect) | |
143 | t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", a, expect) | |
143 | 144 | } |
144 | 145 | } |
145 | 146 | |
148 | 149 | s2 := simpleTest{19} |
149 | 150 | a := pointerTest{&s1} |
150 | 151 | b := pointerTest{&s2} |
151 | if err := Merge(&a, b); err != nil { | |
152 | if err := mergo.Merge(&a, b); err != nil { | |
152 | 153 | t.FailNow() |
153 | 154 | } |
154 | 155 | if a.C.Value != b.C.Value { |
155 | t.Fatalf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) | |
156 | t.Errorf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) | |
156 | 157 | } |
157 | 158 | } |
158 | 159 | |
206 | 207 | } |
207 | 208 | |
208 | 209 | for _, test := range tests { |
209 | err := Merge(&test.dst, test.src) | |
210 | err := mergo.Merge(&test.dst, test.src) | |
210 | 211 | if err != nil { |
211 | 212 | t.Errorf("unexpected error: %v", err) |
212 | 213 | continue |
220 | 221 | func TestPointerStructNil(t *testing.T) { |
221 | 222 | a := pointerTest{nil} |
222 | 223 | b := pointerTest{&simpleTest{19}} |
223 | if err := Merge(&a, b); err != nil { | |
224 | if err := mergo.Merge(&a, b); err != nil { | |
224 | 225 | t.FailNow() |
225 | 226 | } |
226 | 227 | if a.C.Value != b.C.Value { |
227 | t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) | |
228 | } | |
229 | } | |
230 | ||
231 | func testSlice(t *testing.T, a []int, b []int, e []int, opts ...func(*Config)) { | |
228 | t.Errorf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) | |
229 | } | |
230 | } | |
231 | ||
232 | func testSlice(t *testing.T, a []int, b []int, e []int, opts ...func(*mergo.Config)) { | |
232 | 233 | t.Helper() |
233 | 234 | bc := b |
234 | 235 | |
235 | 236 | sa := sliceTest{a} |
236 | 237 | sb := sliceTest{b} |
237 | if err := Merge(&sa, sb, opts...); err != nil { | |
238 | if err := mergo.Merge(&sa, sb, opts...); err != nil { | |
238 | 239 | t.FailNow() |
239 | 240 | } |
240 | 241 | if !reflect.DeepEqual(sb.S, bc) { |
241 | t.Fatalf("Source slice was modified %d != %d", sb.S, bc) | |
242 | t.Errorf("Source slice was modified %d != %d", sb.S, bc) | |
242 | 243 | } |
243 | 244 | if !reflect.DeepEqual(sa.S, e) { |
244 | t.Fatalf("b not merged in a proper way %d != %d", sa.S, e) | |
245 | t.Errorf("b not merged in a proper way %d != %d", sa.S, e) | |
245 | 246 | } |
246 | 247 | |
247 | 248 | ma := map[string][]int{"S": a} |
248 | 249 | mb := map[string][]int{"S": b} |
249 | if err := Merge(&ma, mb, opts...); err != nil { | |
250 | if err := mergo.Merge(&ma, mb, opts...); err != nil { | |
250 | 251 | t.FailNow() |
251 | 252 | } |
252 | 253 | if !reflect.DeepEqual(mb["S"], bc) { |
253 | t.Fatalf("map value: Source slice was modified %d != %d", mb["S"], bc) | |
254 | t.Errorf("map value: Source slice was modified %d != %d", mb["S"], bc) | |
254 | 255 | } |
255 | 256 | if !reflect.DeepEqual(ma["S"], e) { |
256 | t.Fatalf("map value: b not merged in a proper way %d != %d", ma["S"], e) | |
257 | t.Errorf("map value: b not merged in a proper way %d != %d", ma["S"], e) | |
257 | 258 | } |
258 | 259 | |
259 | 260 | if a == nil { |
260 | 261 | // test case with missing dst key |
261 | 262 | ma := map[string][]int{} |
262 | 263 | mb := map[string][]int{"S": b} |
263 | if err := Merge(&ma, mb); err != nil { | |
264 | if err := mergo.Merge(&ma, mb); err != nil { | |
264 | 265 | t.FailNow() |
265 | 266 | } |
266 | 267 | if !reflect.DeepEqual(mb["S"], bc) { |
267 | t.Fatalf("missing dst key: Source slice was modified %d != %d", mb["S"], bc) | |
268 | t.Errorf("missing dst key: Source slice was modified %d != %d", mb["S"], bc) | |
268 | 269 | } |
269 | 270 | if !reflect.DeepEqual(ma["S"], e) { |
270 | t.Fatalf("missing dst key: b not merged in a proper way %d != %d", ma["S"], e) | |
271 | t.Errorf("missing dst key: b not merged in a proper way %d != %d", ma["S"], e) | |
271 | 272 | } |
272 | 273 | } |
273 | 274 | |
275 | 276 | // test case with missing src key |
276 | 277 | ma := map[string][]int{"S": a} |
277 | 278 | mb := map[string][]int{} |
278 | if err := Merge(&ma, mb); err != nil { | |
279 | if err := mergo.Merge(&ma, mb); err != nil { | |
279 | 280 | t.FailNow() |
280 | 281 | } |
281 | 282 | if !reflect.DeepEqual(mb["S"], bc) { |
282 | t.Fatalf("missing src key: Source slice was modified %d != %d", mb["S"], bc) | |
283 | t.Errorf("missing src key: Source slice was modified %d != %d", mb["S"], bc) | |
283 | 284 | } |
284 | 285 | if !reflect.DeepEqual(ma["S"], e) { |
285 | t.Fatalf("missing src key: b not merged in a proper way %d != %d", ma["S"], e) | |
286 | t.Errorf("missing src key: b not merged in a proper way %d != %d", ma["S"], e) | |
286 | 287 | } |
287 | 288 | } |
288 | 289 | } |
293 | 294 | testSlice(t, []int{1}, []int{2, 3}, []int{1}) |
294 | 295 | testSlice(t, []int{1}, []int{}, []int{1}) |
295 | 296 | testSlice(t, []int{1}, nil, []int{1}) |
296 | testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}, WithAppendSlice) | |
297 | testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}, WithAppendSlice) | |
298 | 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) | |
300 | testSlice(t, []int{1}, []int{}, []int{1}, WithAppendSlice) | |
301 | testSlice(t, []int{1}, nil, []int{1}, WithAppendSlice) | |
297 | testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice) | |
298 | testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice) | |
299 | testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice) | |
300 | testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice, mergo.WithOverride) | |
301 | testSlice(t, []int{1}, []int{}, []int{1}, mergo.WithAppendSlice) | |
302 | testSlice(t, []int{1}, nil, []int{1}, mergo.WithAppendSlice) | |
302 | 303 | } |
303 | 304 | |
304 | 305 | func TestEmptyMaps(t *testing.T) { |
306 | 307 | b := mapTest{ |
307 | 308 | map[int]int{}, |
308 | 309 | } |
309 | if err := Merge(&a, b); err != nil { | |
310 | if err := mergo.Merge(&a, b); err != nil { | |
310 | 311 | t.Fail() |
311 | 312 | } |
312 | 313 | if !reflect.DeepEqual(a, b) { |
317 | 318 | func TestEmptyToEmptyMaps(t *testing.T) { |
318 | 319 | a := mapTest{} |
319 | 320 | b := mapTest{} |
320 | if err := Merge(&a, b); err != nil { | |
321 | if err := mergo.Merge(&a, b); err != nil { | |
321 | 322 | t.Fail() |
322 | 323 | } |
323 | 324 | if !reflect.DeepEqual(a, b) { |
337 | 338 | b := mapTest{ |
338 | 339 | map[int]int{}, |
339 | 340 | } |
340 | if err := Merge(&a, b); err != nil { | |
341 | if err := mergo.Merge(&a, b); err != nil { | |
341 | 342 | t.Fail() |
342 | 343 | } |
343 | 344 | if !reflect.DeepEqual(a, aa) { |
366 | 367 | "e": {14}, |
367 | 368 | } |
368 | 369 | |
369 | if err := MergeWithOverwrite(&m, n); err != nil { | |
370 | t.Fatalf(err.Error()) | |
370 | if err := mergo.MergeWithOverwrite(&m, n); err != nil { | |
371 | t.Errorf(err.Error()) | |
371 | 372 | } |
372 | 373 | |
373 | 374 | if !reflect.DeepEqual(m, expect) { |
374 | t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
375 | t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
375 | 376 | } |
376 | 377 | } |
377 | 378 | |
396 | 397 | "e": {14}, |
397 | 398 | } |
398 | 399 | |
399 | if err := Merge(&m, n, WithOverride); err != nil { | |
400 | t.Fatalf(err.Error()) | |
401 | } | |
402 | ||
403 | assert.Equalf(t, expect, m, "Test Failed") | |
400 | if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil { | |
401 | t.Errorf(err.Error()) | |
402 | } | |
403 | ||
404 | 404 | if !reflect.DeepEqual(m, expect) { |
405 | t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
405 | t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
406 | 406 | } |
407 | 407 | } |
408 | 408 | |
523 | 523 | t.Run(tc.name, func(t *testing.T) { |
524 | 524 | var err error |
525 | 525 | if tc.overwrite { |
526 | err = Merge(tc.target, *tc.changes, WithOverride) | |
526 | err = mergo.Merge(tc.target, *tc.changes, mergo.WithOverride) | |
527 | 527 | } else { |
528 | err = Merge(tc.target, *tc.changes) | |
528 | err = mergo.Merge(tc.target, *tc.changes) | |
529 | 529 | } |
530 | 530 | if err != nil { |
531 | 531 | t.Error(err) |
532 | 532 | } |
533 | 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) | |
534 | t.Errorf("Test failed:\ngot :\n%+v\n\nwant :\n%+v\n\n", tc.target.Params, tc.output.Params) | |
535 | 535 | } |
536 | 536 | }) |
537 | 537 | } |
557 | 557 | "e": {14}, |
558 | 558 | } |
559 | 559 | |
560 | if err := Merge(&m, n); err != nil { | |
561 | t.Fatalf(err.Error()) | |
560 | if err := mergo.Merge(&m, n); err != nil { | |
561 | t.Errorf(err.Error()) | |
562 | 562 | } |
563 | 563 | |
564 | 564 | if !reflect.DeepEqual(m, expect) { |
565 | t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
565 | t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
566 | 566 | } |
567 | 567 | if m["a"].Value != 0 { |
568 | t.Fatalf(`n merged in m because I solved non-addressable map values TODO: m["a"].Value(%d) != n["a"].Value(%d)`, m["a"].Value, n["a"].Value) | |
568 | t.Errorf(`n merged in m because I solved non-addressable map values TODO: m["a"].Value(%d) != n["a"].Value(%d)`, m["a"].Value, n["a"].Value) | |
569 | 569 | } |
570 | 570 | if m["b"].Value != 42 { |
571 | t.Fatalf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value) | |
571 | t.Errorf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value) | |
572 | 572 | } |
573 | 573 | if m["c"].Value != 13 { |
574 | t.Fatalf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value) | |
574 | t.Errorf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value) | |
575 | 575 | } |
576 | 576 | } |
577 | 577 | |
590 | 590 | "c": nil, |
591 | 591 | } |
592 | 592 | |
593 | if err := Merge(&m, n, WithOverride); err != nil { | |
594 | t.Fatalf(err.Error()) | |
593 | if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil { | |
594 | t.Errorf(err.Error()) | |
595 | 595 | } |
596 | 596 | |
597 | 597 | if !reflect.DeepEqual(m, expect) { |
598 | t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
598 | t.Errorf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect) | |
599 | 599 | } |
600 | 600 | } |
601 | 601 | |
606 | 606 | fl := license["fields"].(map[interface{}]interface{}) |
607 | 607 | // license has one extra field (site) and another already existing in thing (author) that Mergo won't override. |
608 | 608 | expectedLength := len(ft) + len(fl) - 1 |
609 | if err := Merge(&license, thing); err != nil { | |
610 | t.Fatal(err.Error()) | |
609 | if err := mergo.Merge(&license, thing); err != nil { | |
610 | t.Error(err.Error()) | |
611 | 611 | } |
612 | 612 | currentLength := len(license["fields"].(map[interface{}]interface{})) |
613 | 613 | if currentLength != expectedLength { |
614 | t.Fatalf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength) | |
614 | t.Errorf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength) | |
615 | 615 | } |
616 | 616 | fields := license["fields"].(map[interface{}]interface{}) |
617 | 617 | if _, ok := fields["id"]; !ok { |
618 | t.Fatalf(`thing not merged in license properly, license must have a new id field from thing`) | |
618 | t.Errorf(`thing not merged in license properly, license must have a new id field from thing`) | |
619 | 619 | } |
620 | 620 | } |
621 | 621 | |
622 | 622 | func TestTwoPointerValues(t *testing.T) { |
623 | 623 | a := &simpleTest{} |
624 | 624 | b := &simpleTest{42} |
625 | if err := Merge(a, b); err != nil { | |
626 | t.Fatalf(`Boom. You crossed the streams: %s`, err) | |
625 | if err := mergo.Merge(a, b); err != nil { | |
626 | t.Errorf(`Boom. You crossed the streams: %s`, err) | |
627 | 627 | } |
628 | 628 | } |
629 | 629 | |
643 | 643 | "zt": simpleTest{299}, // Mapping a missing field (zt doesn't exist) |
644 | 644 | "nt": simpleTest{3}, |
645 | 645 | } |
646 | if err := Map(&c, b); err != nil { | |
646 | if err := mergo.Map(&c, b); err != nil { | |
647 | 647 | t.FailNow() |
648 | 648 | } |
649 | 649 | m := b["ct"].(map[string]interface{}) |
651 | 651 | o := b["st"].(*simpleTest) |
652 | 652 | p := b["nt"].(simpleTest) |
653 | 653 | if c.Ct.St.Value != 42 { |
654 | t.Fatalf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"]) | |
654 | t.Errorf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"]) | |
655 | 655 | } |
656 | 656 | if c.St.Value != 144 { |
657 | t.Fatalf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value) | |
657 | t.Errorf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value) | |
658 | 658 | } |
659 | 659 | if c.Nt.Value != 3 { |
660 | t.Fatalf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value) | |
660 | t.Errorf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value) | |
661 | 661 | } |
662 | 662 | if c.Ct.sz == 1 { |
663 | t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"]) | |
663 | t.Errorf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"]) | |
664 | 664 | } |
665 | 665 | if c.Ct.ID == m["id"] { |
666 | t.Fatalf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"]) | |
666 | t.Errorf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"]) | |
667 | 667 | } |
668 | 668 | } |
669 | 669 | |
672 | 672 | b := map[string]interface{}{ |
673 | 673 | "value": 42, |
674 | 674 | } |
675 | if err := Map(&a, b); err != nil { | |
675 | if err := mergo.Map(&a, b); err != nil { | |
676 | 676 | t.FailNow() |
677 | 677 | } |
678 | 678 | if a.Value != 42 { |
679 | t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"]) | |
679 | t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"]) | |
680 | 680 | } |
681 | 681 | } |
682 | 682 | |
683 | 683 | func TestIfcMap(t *testing.T) { |
684 | 684 | a := ifcTest{} |
685 | 685 | b := ifcTest{42} |
686 | if err := Map(&a, b); err != nil { | |
686 | if err := mergo.Map(&a, b); err != nil { | |
687 | 687 | t.FailNow() |
688 | 688 | } |
689 | 689 | if a.I != 42 { |
690 | t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) | |
690 | t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) | |
691 | 691 | } |
692 | 692 | if !reflect.DeepEqual(a, b) { |
693 | 693 | t.FailNow() |
697 | 697 | func TestIfcMapNoOverwrite(t *testing.T) { |
698 | 698 | a := ifcTest{13} |
699 | 699 | b := ifcTest{42} |
700 | if err := Map(&a, b); err != nil { | |
700 | if err := mergo.Map(&a, b); err != nil { | |
701 | 701 | t.FailNow() |
702 | 702 | } |
703 | 703 | if a.I != 13 { |
704 | t.Fatalf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I) | |
704 | t.Errorf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I) | |
705 | 705 | } |
706 | 706 | } |
707 | 707 | |
708 | 708 | func TestIfcMapWithOverwrite(t *testing.T) { |
709 | 709 | a := ifcTest{13} |
710 | 710 | b := ifcTest{42} |
711 | if err := MapWithOverwrite(&a, b); err != nil { | |
711 | if err := mergo.MapWithOverwrite(&a, b); err != nil { | |
712 | 712 | t.FailNow() |
713 | 713 | } |
714 | 714 | if a.I != 42 { |
715 | t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) | |
715 | t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I) | |
716 | 716 | } |
717 | 717 | if !reflect.DeepEqual(a, b) { |
718 | 718 | t.FailNow() |
728 | 728 | func TestBackAndForth(t *testing.T) { |
729 | 729 | pt := pointerMapTest{42, 1, &simpleTest{66}} |
730 | 730 | m := make(map[string]interface{}) |
731 | if err := Map(&m, pt); err != nil { | |
731 | if err := mergo.Map(&m, pt); err != nil { | |
732 | 732 | t.FailNow() |
733 | 733 | } |
734 | 734 | var ( |
736 | 736 | ok bool |
737 | 737 | ) |
738 | 738 | if v, ok = m["a"]; v.(int) != pt.A || !ok { |
739 | t.Fatalf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A) | |
739 | t.Errorf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A) | |
740 | 740 | } |
741 | 741 | if v, ok = m["b"]; !ok { |
742 | t.Fatalf("pt not merged in properly: B is missing in m") | |
742 | t.Errorf("pt not merged in properly: B is missing in m") | |
743 | 743 | } |
744 | 744 | var st *simpleTest |
745 | 745 | if st = v.(*simpleTest); st.Value != 66 { |
746 | t.Fatalf("something went wrong while mapping pt on m, B wasn't copied") | |
746 | t.Errorf("something went wrong while mapping pt on m, B wasn't copied") | |
747 | 747 | } |
748 | 748 | bpt := pointerMapTest{} |
749 | if err := Map(&bpt, m); err != nil { | |
750 | t.Fatal(err) | |
749 | if err := mergo.Map(&bpt, m); err != nil { | |
750 | t.Error(err) | |
751 | 751 | } |
752 | 752 | if bpt.A != pt.A { |
753 | t.Fatalf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A) | |
753 | t.Errorf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A) | |
754 | 754 | } |
755 | 755 | if bpt.hidden == pt.hidden { |
756 | t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden) | |
756 | t.Errorf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden) | |
757 | 757 | } |
758 | 758 | if bpt.B.Value != pt.B.Value { |
759 | t.Fatalf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value) | |
759 | t.Errorf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value) | |
760 | 760 | } |
761 | 761 | } |
762 | 762 | |
773 | 773 | } |
774 | 774 | for _, test := range tests { |
775 | 775 | pt := test.input |
776 | if err := MapWithOverwrite(&pt, m); err != nil { | |
776 | if err := mergo.MapWithOverwrite(&pt, m); err != nil { | |
777 | 777 | t.FailNow() |
778 | 778 | } |
779 | 779 | if pt.B.Value != newValue { |
780 | t.Fatalf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue) | |
780 | t.Errorf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue) | |
781 | 781 | } |
782 | 782 | |
783 | 783 | } |
796 | 796 | "Birth": &now, |
797 | 797 | } |
798 | 798 | b := structWithTimePointer{} |
799 | if err := Merge(&b, dataStruct); err != nil { | |
799 | if err := mergo.Merge(&b, dataStruct); err != nil { | |
800 | 800 | t.FailNow() |
801 | 801 | } |
802 | 802 | if b.Birth.IsZero() { |
803 | t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) | |
803 | t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) | |
804 | 804 | } |
805 | 805 | if b.Birth != dataStruct.Birth { |
806 | t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) | |
806 | t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth) | |
807 | 807 | } |
808 | 808 | b = structWithTimePointer{} |
809 | if err := Map(&b, dataMap); err != nil { | |
809 | if err := mergo.Map(&b, dataMap); err != nil { | |
810 | 810 | t.FailNow() |
811 | 811 | } |
812 | 812 | if b.Birth.IsZero() { |
813 | t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"]) | |
813 | t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"]) | |
814 | 814 | } |
815 | 815 | } |
816 | 816 | |
835 | 835 | "x": {}, |
836 | 836 | }, |
837 | 837 | } |
838 | if err := Map(dst, src); err != nil { | |
838 | if err := mergo.Map(dst, src); err != nil { | |
839 | 839 | t.FailNow() |
840 | 840 | } |
841 | 841 | if dst.NestedPtrValue["x"].A == 0 { |
842 | 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) | |
842 | t.Errorf("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) | |
843 | 843 | } |
844 | 844 | } |
845 | 845 | |
870 | 870 | t.Errorf("Should not have panicked") |
871 | 871 | } |
872 | 872 | }() |
873 | Merge(&a, b) | |
873 | mergo.Merge(&a, b) | |
874 | 874 | } |
875 | 875 | |
876 | 876 | type structWithBoolPointer struct { |
885 | 885 | dst := structWithBoolPointer{ |
886 | 886 | &bf, |
887 | 887 | } |
888 | if err := Merge(&dst, src); err != nil { | |
888 | if err := mergo.Merge(&dst, src); err != nil { | |
889 | 889 | t.FailNow() |
890 | 890 | } |
891 | 891 | if dst.C == src.C { |
892 | t.Fatalf("dst.C should be a different pointer than src.C") | |
892 | t.Errorf("dst.C should be a different pointer than src.C") | |
893 | 893 | } |
894 | 894 | if *dst.C != *src.C { |
895 | t.Fatalf("dst.C should be true") | |
895 | t.Errorf("dst.C should be true") | |
896 | 896 | } |
897 | 897 | } |
898 | 898 | |
899 | 899 | func TestMergeMapWithInnerSliceOfDifferentType(t *testing.T) { |
900 | 900 | testCases := []struct { |
901 | 901 | name string |
902 | options []func(*Config) | |
902 | options []func(*mergo.Config) | |
903 | 903 | err string |
904 | 904 | }{ |
905 | 905 | { |
906 | 906 | "With override and append slice", |
907 | []func(*Config){WithOverride, WithAppendSlice}, | |
907 | []func(*mergo.Config){mergo.WithOverride, mergo.WithAppendSlice}, | |
908 | 908 | "cannot append two slices with different type", |
909 | 909 | }, |
910 | 910 | { |
911 | 911 | "With override and type check", |
912 | []func(*Config){WithOverride, WithTypeCheck}, | |
912 | []func(*mergo.Config){mergo.WithOverride, mergo.WithTypeCheck}, | |
913 | 913 | "cannot override two slices with different type", |
914 | 914 | }, |
915 | 915 | } |
922 | 922 | "foo": []int{1, 2}, |
923 | 923 | } |
924 | 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) | |
925 | if err := mergo.Merge(&src, &dst, tc.options...); err == nil || !strings.Contains(err.Error(), tc.err) { | |
926 | t.Errorf("expected %q, got %q", tc.err, err) | |
927 | 927 | } |
928 | 928 | }) |
929 | 929 | } |
933 | 933 | src := []string{"a", "b"} |
934 | 934 | dst := []int{1, 2} |
935 | 935 | |
936 | if err := Merge(&src, &dst, WithOverride, WithAppendSlice); err != ErrNotSupported { | |
937 | t.Fatalf("expected %q, got %q", ErrNotSupported, err) | |
938 | } | |
939 | } | |
936 | if err := mergo.Merge(&src, &dst, mergo.WithOverride, mergo.WithAppendSlice); err != mergo.ErrNotSupported { | |
937 | t.Errorf("expected %q, got %q", mergo.ErrNotSupported, err) | |
938 | } | |
939 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | ||
5 | "github.com/imdario/mergo" | |
4 | 6 | ) |
5 | 7 | |
6 | 8 | type mapInterface map[string]interface{} |
8 | 10 | func TestMergeMapsEmptyString(t *testing.T) { |
9 | 11 | a := mapInterface{"s": ""} |
10 | 12 | b := mapInterface{"s": "foo"} |
11 | if err := Merge(&a, b); err != nil { | |
12 | t.Fatal(err) | |
13 | if err := mergo.Merge(&a, b); err != nil { | |
14 | t.Error(err) | |
13 | 15 | } |
14 | 16 | if a["s"] != "foo" { |
15 | t.Fatalf("b not merged in properly: a.s.Value(%s) != expected(%s)", a["s"], "foo") | |
17 | t.Errorf("b not merged in properly: a.s.Value(%s) != expected(%s)", a["s"], "foo") | |
16 | 18 | } |
17 | 19 | } |
0 | package mergo | |
0 | package mergo_test | |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "testing" |
4 | ||
5 | "github.com/imdario/mergo" | |
4 | 6 | ) |
5 | 7 | |
6 | 8 | func TestMapInterfaceWithMultipleLayer(t *testing.T) { |
17 | 19 | }, |
18 | 20 | } |
19 | 21 | |
20 | if err := Map(&m1, m2, WithOverride); err != nil { | |
21 | t.Fatalf("Error merging: %v", err) | |
22 | if err := mergo.Map(&m1, m2, mergo.WithOverride); err != nil { | |
23 | t.Errorf("Error merging: %v", err) | |
22 | 24 | } |
23 | 25 | |
24 | 26 | // Check overwrite of sub map works |
25 | 27 | expected := "v2" |
26 | 28 | actual := m1["k1"].(map[string]interface{})["k1.1"].(string) |
27 | 29 | if actual != expected { |
28 | t.Fatalf("Expected %v but got %v", | |
30 | t.Errorf("Expected %v but got %v", | |
29 | 31 | expected, |
30 | 32 | actual) |
31 | 33 | } |
34 | 36 | expected = "v3" |
35 | 37 | actual = m1["k1"].(map[string]interface{})["k1.2"].(string) |
36 | 38 | if actual != expected { |
37 | t.Fatalf("Expected %v but got %v", | |
39 | t.Errorf("Expected %v but got %v", | |
38 | 40 | expected, |
39 | 41 | actual) |
40 | 42 | } |
0 | package mergo_test | |
1 | ||
2 | import ( | |
3 | "testing" | |
4 | ||
5 | "github.com/imdario/mergo" | |
6 | ) | |
7 | ||
8 | type inner struct { | |
9 | A int | |
10 | } | |
11 | ||
12 | type outer struct { | |
13 | inner | |
14 | B int | |
15 | } | |
16 | ||
17 | func TestV039Issue139(t *testing.T) { | |
18 | dst := outer{ | |
19 | inner: inner{A: 1}, | |
20 | B: 2, | |
21 | } | |
22 | src := outer{ | |
23 | inner: inner{A: 10}, | |
24 | B: 20, | |
25 | } | |
26 | err := mergo.MergeWithOverwrite(&dst, src) | |
27 | if err != nil { | |
28 | panic(err.Error()) | |
29 | } | |
30 | if dst.inner.A == 1 { | |
31 | t.Errorf("expected %d, got %d", src.inner.A, dst.inner.A) | |
32 | } | |
33 | } | |
34 | ||
35 | func TestV039Issue152(t *testing.T) { | |
36 | dst := map[string]interface{}{ | |
37 | "properties": map[string]interface{}{ | |
38 | "field1": map[string]interface{}{ | |
39 | "type": "text", | |
40 | }, | |
41 | "field2": "ohai", | |
42 | }, | |
43 | } | |
44 | src := map[string]interface{}{ | |
45 | "properties": map[string]interface{}{ | |
46 | "field1": "wrong", | |
47 | }, | |
48 | } | |
49 | if err := mergo.Map(&dst, src, mergo.WithOverride); err != nil { | |
50 | t.Error(err) | |
51 | } | |
52 | } | |
53 | ||
54 | type issue146Foo struct { | |
55 | A string | |
56 | B map[string]issue146Bar | |
57 | } | |
58 | ||
59 | type issue146Bar struct { | |
60 | C *string | |
61 | D *string | |
62 | } | |
63 | ||
64 | func TestV039Issue146(t *testing.T) { | |
65 | var ( | |
66 | s1 = "asd" | |
67 | s2 = "sdf" | |
68 | ) | |
69 | dst := issue146Foo{ | |
70 | A: "two", | |
71 | B: map[string]issue146Bar{ | |
72 | "foo": { | |
73 | C: &s1, | |
74 | }, | |
75 | }, | |
76 | } | |
77 | src := issue146Foo{ | |
78 | A: "one", | |
79 | B: map[string]issue146Bar{ | |
80 | "foo": { | |
81 | D: &s2, | |
82 | }, | |
83 | }, | |
84 | } | |
85 | if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { | |
86 | t.Error(err) | |
87 | } | |
88 | if dst.B["foo"].D == nil { | |
89 | t.Errorf("expected %v, got nil", &s2) | |
90 | } | |
91 | } |