Codebase list golang-github-imdario-mergo / 6720d30
New upstream version 0.3.3 Arnaud Rebillout 5 years ago
20 changed file(s) with 974 addition(s) and 95 deletion(s). Raw diff Collapse all Expand all
0 #### joe made this: http://goel.io/joe
1
2 #### go ####
3 # Binaries for programs and plugins
4 *.exe
5 *.dll
6 *.so
7 *.dylib
8
9 # Test binary, build with `go test -c`
10 *.test
11
12 # Output of the go coverage tool, specifically when used with LiteIDE
13 *.out
14
15 # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
16 .glide/
17
18 #### vim ####
19 # Swap
20 [._]*.s[a-v][a-z]
21 [._]*.sw[a-p]
22 [._]s[a-v][a-z]
23 [._]sw[a-p]
24
25 # Session
26 Session.vim
27
28 # Temporary
29 .netrwhist
30 *~
31 # Auto-generated tag files
32 tags
00 language: go
1 install: go get -t
1 install:
2 - go get -t
3 - go get golang.org/x/tools/cmd/cover
4 - go get github.com/mattn/goveralls
5 script:
6 - $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN
0 # Contributor Covenant Code of Conduct
1
2 ## Our Pledge
3
4 In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
5
6 ## Our Standards
7
8 Examples of behavior that contributes to creating a positive environment include:
9
10 * Using welcoming and inclusive language
11 * Being respectful of differing viewpoints and experiences
12 * Gracefully accepting constructive criticism
13 * Focusing on what is best for the community
14 * Showing empathy towards other community members
15
16 Examples of unacceptable behavior by participants include:
17
18 * The use of sexualized language or imagery and unwelcome sexual attention or advances
19 * Trolling, insulting/derogatory comments, and personal or political attacks
20 * Public or private harassment
21 * Publishing others' private information, such as a physical or electronic address, without explicit permission
22 * Other conduct which could reasonably be considered inappropriate in a professional setting
23
24 ## Our Responsibilities
25
26 Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
27
28 Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
29
30 ## Scope
31
32 This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
33
34 ## Enforcement
35
36 Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at i@dario.im. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
37
38 Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
39
40 ## Attribution
41
42 This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
43
44 [homepage]: http://contributor-covenant.org
45 [version]: http://contributor-covenant.org/version/1/4/
11
22 A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
33
4 Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region Marche.
5
6 ![Mergo dall'alto](http://www.comune.mergo.an.it/Siti/Mergo/Immagini/Foto/mergo_dall_alto.jpg)
4 Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche.
75
86 ## Status
97
10 It is ready for production use. It works fine after extensive use in the wild.
11
12 [![Build Status][1]][2]
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
1310 [![GoDoc][3]][4]
1411 [![GoCard][5]][6]
12 [![Build Status][1]][2]
13 [![Coverage Status][7]][8]
14 [![Sourcegraph][9]][10]
1515
1616 [1]: https://travis-ci.org/imdario/mergo.png
1717 [2]: https://travis-ci.org/imdario/mergo
1919 [4]: https://godoc.org/github.com/imdario/mergo
2020 [5]: https://goreportcard.com/badge/imdario/mergo
2121 [6]: https://goreportcard.com/report/github.com/imdario/mergo
22 [7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master
23 [8]: https://coveralls.io/github/imdario/mergo?branch=master
24 [9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg
25 [10]: https://sourcegraph.com/github.com/imdario/mergo?badge
26
27 ### Latest release
28
29 [Release v0.3.3](https://github.com/imdario/mergo/releases/tag/v0.3.3).
2230
2331 ### Important note
2432
25 Mergo is intended to assign **only** zero value fields on destination with source value. Since April 6th it works like this. Before it didn't work properly, causing some random overwrites. After some issues and PRs I found it didn't merge as I designed it. Thanks to [imdario/mergo#8](https://github.com/imdario/mergo/pull/8) overwriting functions were added and the wrong behavior was clearly detected.
33 Please keep in mind that in [v0.3.2](//github.com/imdario/mergo/releases/tag/v0.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.
2634
2735 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).
2836
37 ### Donations
38
39 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:
40
41 <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>
42 [![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo)
43 [![Beerpay](https://beerpay.io/imdario/mergo/make-wish.svg)](https://beerpay.io/imdario/mergo)
44 <a href="https://liberapay.com/dario/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
45
2946 ### Mergo in the wild
3047
31 - [docker/docker](https://github.com/docker/docker/)
32 - [GoogleCloudPlatform/kubernetes](https://github.com/GoogleCloudPlatform/kubernetes)
48 - [moby/moby](https://github.com/moby/moby)
49 - [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
50 - [vmware/dispatch](https://github.com/vmware/dispatch)
51 - [Shopify/themekit](https://github.com/Shopify/themekit)
3352 - [imdario/zas](https://github.com/imdario/zas)
53 - [matcornic/hermes](https://github.com/matcornic/hermes)
54 - [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go)
55 - [kataras/iris](https://github.com/kataras/iris)
56 - [michaelsauter/crane](https://github.com/michaelsauter/crane)
57 - [go-task/task](https://github.com/go-task/task)
58 - [sensu/uchiwa](https://github.com/sensu/uchiwa)
59 - [ory/hydra](https://github.com/ory/hydra)
60 - [sisatech/vcli](https://github.com/sisatech/vcli)
61 - [dairycart/dairycart](https://github.com/dairycart/dairycart)
62 - [projectcalico/felix](https://github.com/projectcalico/felix)
63 - [resin-os/balena](https://github.com/resin-os/balena)
64 - [go-kivik/kivik](https://github.com/go-kivik/kivik)
65 - [Telefonica/govice](https://github.com/Telefonica/govice)
66 - [supergiant/supergiant](supergiant/supergiant)
67 - [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce)
3468 - [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
69 - [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel)
3570 - [EagerIO/Stout](https://github.com/EagerIO/Stout)
3671 - [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
3772 - [russross/canvasassignments](https://github.com/russross/canvasassignments)
4984 - [thoas/picfit](https://github.com/thoas/picfit)
5085 - [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
5186 - [jnuthong/item_search](https://github.com/jnuthong/item_search)
87 - [bukalapak/snowboard](https://github.com/bukalapak/snowboard)
5288
5389 ## Installation
5490
6197
6298 ## Usage
6399
64 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. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
65
66 if err := mergo.Merge(&dst, src); err != nil {
67 // ...
68 }
69
70 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.
71
72 if err := mergo.Map(&dst, srcMap); err != nil {
73 // ...
74 }
75
76 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.
100 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).
101
102 ```go
103 if err := mergo.Merge(&dst, src); err != nil {
104 // ...
105 }
106 ```
107
108 Also, you can merge overwriting values using the transformer `WithOverride`.
109
110 ```go
111 if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
112 // ...
113 }
114 ```
115
116 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.
117
118 ```go
119 if err := mergo.Map(&dst, srcMap); err != nil {
120 // ...
121 }
122 ```
123
124 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.
77125
78126 More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo).
79127
95143 func main() {
96144 src := Foo{
97145 A: "one",
146 B: 2,
98147 }
99
100148 dest := Foo{
101149 A: "two",
102 B: 2,
103150 }
104
105151 mergo.Merge(&dest, src)
106
107152 fmt.Println(dest)
108153 // Will print
109154 // {two 2}
112157
113158 Note: if test are failing due missing package, please execute:
114159
115 go get gopkg.in/yaml.v1
160 go get gopkg.in/yaml.v2
161
162 ### Transformers
163
164 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`?
165
166 ```go
167 package main
168
169 import (
170 "fmt"
171 "github.com/imdario/mergo"
172 "reflect"
173 "time"
174 )
175
176 type timeTransfomer struct {
177 }
178
179 func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
180 if typ == reflect.TypeOf(time.Time{}) {
181 return func(dst, src reflect.Value) error {
182 if dst.CanSet() {
183 isZero := dst.MethodByName("IsZero")
184 result := isZero.Call([]reflect.Value{})
185 if result[0].Bool() {
186 dst.Set(src)
187 }
188 }
189 return nil
190 }
191 }
192 return nil
193 }
194
195 type Snapshot struct {
196 Time time.Time
197 // ...
198 }
199
200 func main() {
201 src := Snapshot{time.Now()}
202 dest := Snapshot{}
203 mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{}))
204 fmt.Println(dest)
205 // Will print
206 // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
207 }
208 ```
209
116210
117211 ## Contact me
118212
1616 func TestIssue17MergeWithOverwrite(t *testing.T) {
1717 var something map[string]interface{}
1818 if err := json.Unmarshal([]byte(request), &something); err != nil {
19 t.Errorf("Error while Unmarshalling maprequest %s", err)
19 t.Errorf("Error while Unmarshalling maprequest: %s", err)
2020 }
2121 if err := MergeWithOverwrite(&something, maprequest); err != nil {
22 t.Errorf("Error while merging %s", err)
22 t.Errorf("Error while merging: %s", err)
2323 }
2424 }
0 package mergo
1
2 import (
3 "testing"
4 "time"
5 )
6
7 type document struct {
8 Created *time.Time
9 }
10
11 func TestIssue23MergeWithOverwrite(t *testing.T) {
12 now := time.Now()
13 dst := document{
14 &now,
15 }
16 expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
17 src := document{
18 &expected,
19 }
20 if err := MergeWithOverwrite(&dst, src); err != nil {
21 t.Errorf("Error while merging %s", err)
22 }
23 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)
25 }
26 }
0 package mergo
1
2 import (
3 "testing"
4 )
5
6 type Foo struct {
7 Str string
8 Bslice []byte
9 }
10
11 func TestIssue33Merge(t *testing.T) {
12 dest := Foo{Str: "a"}
13 toMerge := Foo{
14 Str: "b",
15 Bslice: []byte{1, 2},
16 }
17 if err := Merge(&dest, toMerge); err != nil {
18 t.Errorf("Error while merging: %s", err)
19 }
20 // Merge doesn't overwrite an attribute if in destination it doesn't have a zero value.
21 // In this case, Str isn't a zero value string.
22 if dest.Str != "a" {
23 t.Errorf("dest.Str should have not been override as it has a non-zero value: dest.Str(%v) != 'a'", dest.Str)
24 }
25 // If we want to override, we must use MergeWithOverwrite or Merge using WithOverride.
26 if err := Merge(&dest, toMerge, WithOverride); err != nil {
27 t.Errorf("Error while merging: %s", err)
28 }
29 if dest.Str != toMerge.Str {
30 t.Errorf("dest.Str should have been override: dest.Str(%v) != toMerge.Str(%v)", dest.Str, toMerge.Str)
31 }
32 }
0 package mergo
1
2 import (
3 "testing"
4 "time"
5 )
6
7 type structWithoutTimePointer struct {
8 Created time.Time
9 }
10
11 func TestIssue38Merge(t *testing.T) {
12 dst := structWithoutTimePointer{
13 time.Now(),
14 }
15
16 expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
17 src := structWithoutTimePointer{
18 expected,
19 }
20 if err := Merge(&dst, src); err != nil {
21 t.Errorf("Error while merging %s", err)
22 }
23 if dst.Created == src.Created {
24 t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
25 }
26 }
27
28 func TestIssue38MergeEmptyStruct(t *testing.T) {
29 dst := structWithoutTimePointer{}
30
31 expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
32 src := structWithoutTimePointer{
33 expected,
34 }
35 if err := Merge(&dst, src); err != nil {
36 t.Errorf("Error while merging %s", err)
37 }
38 if dst.Created == src.Created {
39 t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
40 }
41 }
42
43 func TestIssue38MergeWithOverwrite(t *testing.T) {
44 dst := structWithoutTimePointer{
45 time.Now(),
46 }
47
48 expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
49 src := structWithoutTimePointer{
50 expected,
51 }
52 if err := MergeWithOverwrite(&dst, src); err != nil {
53 t.Errorf("Error while merging %s", err)
54 }
55 if dst.Created != src.Created {
56 t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
57 }
58 }
0 package mergo
1
2 import (
3 "testing"
4 "time"
5 )
6
7 type testStruct struct {
8 time.Duration
9 }
10
11 func TestIssue50Merge(t *testing.T) {
12 to := testStruct{}
13 from := testStruct{}
14 if err := Merge(&to, from); err != nil {
15 t.Fail()
16 }
17 }
0 package mergo
1
2 import (
3 "reflect"
4 "testing"
5 "time"
6 )
7
8 type structWithTime struct {
9 Birth time.Time
10 }
11
12 type timeTransfomer struct {
13 overwrite bool
14 }
15
16 func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
17 if typ == reflect.TypeOf(time.Time{}) {
18 return func(dst, src reflect.Value) error {
19 if dst.CanSet() {
20 if t.overwrite {
21 isZero := src.MethodByName("IsZero")
22 result := isZero.Call([]reflect.Value{})
23 if !result[0].Bool() {
24 dst.Set(src)
25 }
26 } else {
27 isZero := dst.MethodByName("IsZero")
28 result := isZero.Call([]reflect.Value{})
29 if result[0].Bool() {
30 dst.Set(src)
31 }
32 }
33 }
34 return nil
35 }
36 }
37 return nil
38 }
39
40 func TestOverwriteZeroSrcTime(t *testing.T) {
41 now := time.Now()
42 dst := structWithTime{now}
43 src := structWithTime{}
44 if err := MergeWithOverwrite(&dst, src); err != nil {
45 t.FailNow()
46 }
47 if !dst.Birth.IsZero() {
48 t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
49 }
50 }
51
52 func TestOverwriteZeroSrcTimeWithTransformer(t *testing.T) {
53 now := time.Now()
54 dst := structWithTime{now}
55 src := structWithTime{}
56 if err := MergeWithOverwrite(&dst, src, WithTransformers(timeTransfomer{true})); err != nil {
57 t.FailNow()
58 }
59 if dst.Birth.IsZero() {
60 t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
61 }
62 }
63
64 func TestOverwriteZeroDstTime(t *testing.T) {
65 now := time.Now()
66 dst := structWithTime{}
67 src := structWithTime{now}
68 if err := MergeWithOverwrite(&dst, src); err != nil {
69 t.FailNow()
70 }
71 if dst.Birth.IsZero() {
72 t.Fatalf("dst should have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{})
73 }
74 }
75
76 func TestZeroDstTime(t *testing.T) {
77 now := time.Now()
78 dst := structWithTime{}
79 src := structWithTime{now}
80 if err := Merge(&dst, src); err != nil {
81 t.FailNow()
82 }
83 if !dst.Birth.IsZero() {
84 t.Fatalf("dst should not have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{})
85 }
86 }
87
88 func TestZeroDstTimeWithTransformer(t *testing.T) {
89 now := time.Now()
90 dst := structWithTime{}
91 src := structWithTime{now}
92 if err := Merge(&dst, src, WithTransformers(timeTransfomer{})); err != nil {
93 t.FailNow()
94 }
95 if dst.Birth.IsZero() {
96 t.Fatalf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
97 }
98 }
0 package mergo
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 func TestIssue61MergeNilMap(t *testing.T) {
8 type T struct {
9 I map[string][]string
10 }
11 t1 := T{}
12 t2 := T{I: map[string][]string{"hi": {"there"}}}
13 if err := Merge(&t1, t2); err != nil {
14 t.Fail()
15 }
16 if !reflect.DeepEqual(t2, T{I: map[string][]string{"hi": {"there"}}}) {
17 t.FailNow()
18 }
19 }
0 package mergo
1
2 import (
3 "testing"
4 )
5
6 type Student struct {
7 Name string
8 Books []string
9 }
10
11 var testData = []struct {
12 S1 Student
13 S2 Student
14 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{}},
20 }
21
22 func TestIssue64MergeSliceWithOverride(t *testing.T) {
23 for _, data := range testData {
24 err := Merge(&data.S2, data.S1, WithOverride)
25 if err != nil {
26 t.Errorf("Error while merging %s", err)
27 }
28 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))
30 }
31 for i, val := range data.S2.Books {
32 if val != data.ExpectedSlice[i] {
33 t.Fatalf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val)
34 }
35 }
36 }
37 }
0 package mergo
1
2 import (
3 "testing"
4 )
5
6 type PrivateSliceTest66 struct {
7 PublicStrings []string
8 privateStrings []string
9 }
10
11 func TestPrivateSlice(t *testing.T) {
12 p1 := PrivateSliceTest66{
13 PublicStrings: []string{"one", "two", "three"},
14 privateStrings: []string{"four", "five"},
15 }
16 p2 := PrivateSliceTest66{
17 PublicStrings: []string{"six", "seven"},
18 }
19 if err := Merge(&p1, p2); err != nil {
20 t.Fatalf("Error during the merge: %v", err)
21 }
22 if len(p1.PublicStrings) != 5 {
23 t.Error("5 elements should be in 'PublicStrings' field")
24 }
25 if len(p1.privateStrings) != 2 {
26 t.Error("2 elements should be in 'privateStrings' field")
27 }
28 }
3030 // Traverses recursively both values, assigning src's fields values to dst.
3131 // The map argument tracks comparisons that have already been seen, which allows
3232 // short circuiting on recursive types.
33 func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
33 func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
34 overwrite := config.Overwrite
3435 if dst.CanAddr() {
3536 addr := dst.UnsafeAddr()
3637 h := 17 * addr
6061 dstMap[fieldName] = src.Field(i).Interface()
6162 }
6263 }
64 case reflect.Ptr:
65 if dst.IsNil() {
66 v := reflect.New(dst.Type().Elem())
67 dst.Set(v)
68 }
69 dst = dst.Elem()
70 fallthrough
6371 case reflect.Struct:
6472 srcMap := src.Interface().(map[string]interface{})
6573 for key := range srcMap {
8492 srcKind = reflect.Ptr
8593 }
8694 }
95
8796 if !srcElement.IsValid() {
8897 continue
8998 }
9099 if srcKind == dstKind {
91 if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
100 if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
101 return
102 }
103 } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
104 if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
105 return
106 }
107 } else if srcKind == reflect.Map {
108 if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
92109 return
93110 }
94111 } else {
95 if srcKind == reflect.Map {
96 if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
97 return
98 }
99 } else {
100 return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
101 }
112 return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
102113 }
103114 }
104115 }
116127 // doesn't apply if dst is a map.
117128 // This is separated method from Merge because it is cleaner and it keeps sane
118129 // semantics: merging equal types, mapping different (restricted) types.
119 func Map(dst, src interface{}) error {
120 return _map(dst, src, false)
130 func Map(dst, src interface{}, opts ...func(*Config)) error {
131 return _map(dst, src, opts...)
121132 }
122133
123 // MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by
134 // MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
124135 // non-empty src attribute values.
125 func MapWithOverwrite(dst, src interface{}) error {
126 return _map(dst, src, true)
136 // Deprecated: Use Map(…) with WithOverride
137 func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
138 return _map(dst, src, append(opts, WithOverride)...)
127139 }
128140
129 func _map(dst, src interface{}, overwrite bool) error {
141 func _map(dst, src interface{}, opts ...func(*Config)) error {
130142 var (
131143 vDst, vSrc reflect.Value
132144 err error
133145 )
146 config := &Config{}
147
148 for _, opt := range opts {
149 opt(config)
150 }
151
134152 if vDst, vSrc, err = resolveValues(dst, src); err != nil {
135153 return err
136154 }
137155 // To be friction-less, we redirect equal-type arguments
138156 // to deepMerge. Only because arguments can be anything.
139157 if vSrc.Kind() == vDst.Kind() {
140 return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
158 return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
141159 }
142160 switch vSrc.Kind() {
143161 case reflect.Struct:
151169 default:
152170 return ErrNotSupported
153171 }
154 return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
172 return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
155173 }
1111 "reflect"
1212 )
1313
14 func hasExportedField(dst reflect.Value) (exported bool) {
15 for i, n := 0, dst.NumField(); i < n; i++ {
16 field := dst.Type().Field(i)
17 if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
18 exported = exported || hasExportedField(dst.Field(i))
19 } else {
20 exported = exported || len(field.PkgPath) == 0
21 }
22 }
23 return
24 }
25
26 type Config struct {
27 Overwrite bool
28 Transformers Transformers
29 }
30
31 type Transformers interface {
32 Transformer(reflect.Type) func(dst, src reflect.Value) error
33 }
34
1435 // Traverses recursively both values, assigning src's fields values to dst.
1536 // The map argument tracks comparisons that have already been seen, which allows
1637 // short circuiting on recursive types.
17 func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
38 func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
39 overwrite := config.Overwrite
40
1841 if !src.IsValid() {
1942 return
2043 }
3154 // Remember, remember...
3255 visited[h] = &visit{addr, typ, seen}
3356 }
57
58 if config.Transformers != nil && !isEmptyValue(dst) {
59 if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
60 err = fn(dst, src)
61 return
62 }
63 }
64
3465 switch dst.Kind() {
3566 case reflect.Struct:
36 for i, n := 0, dst.NumField(); i < n; i++ {
37 if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
38 return
67 if hasExportedField(dst) {
68 for i, n := 0, dst.NumField(); i < n; i++ {
69 if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
70 return
71 }
72 }
73 } else {
74 if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
75 dst.Set(src)
3976 }
4077 }
4178 case reflect.Map:
79 if dst.IsNil() && !src.IsNil() {
80 dst.Set(reflect.MakeMap(dst.Type()))
81 }
4282 for _, key := range src.MapKeys() {
4383 srcElement := src.MapIndex(key)
4484 if !srcElement.IsValid() {
4686 }
4787 dstElement := dst.MapIndex(key)
4888 switch srcElement.Kind() {
49 case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
89 case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
5090 if srcElement.IsNil() {
5191 continue
5292 }
61101 case reflect.Ptr:
62102 fallthrough
63103 case reflect.Map:
64 if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
104 if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
65105 return
66106 }
67 }
68 }
69 if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
107 case reflect.Slice:
108 srcSlice := reflect.ValueOf(srcElement.Interface())
109
110 var dstSlice reflect.Value
111 if !dstElement.IsValid() || dstElement.IsNil() {
112 dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
113 } else {
114 dstSlice = reflect.ValueOf(dstElement.Interface())
115 }
116
117 dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
118 dst.SetMapIndex(key, dstSlice)
119 }
120 }
121 if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
122 continue
123 }
124
125 if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
70126 if dst.IsNil() {
71127 dst.Set(reflect.MakeMap(dst.Type()))
72128 }
73129 dst.SetMapIndex(key, srcElement)
74130 }
131 }
132 case reflect.Slice:
133 if !dst.CanSet() {
134 break
135 }
136 if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
137 dst.Set(src)
138 } else {
139 dst.Set(reflect.AppendSlice(dst, src))
75140 }
76141 case reflect.Ptr:
77142 fallthrough
78143 case reflect.Interface:
79144 if src.IsNil() {
80145 break
81 } else if dst.IsNil() {
146 }
147 if src.Kind() != reflect.Interface {
148 if dst.IsNil() || overwrite {
149 if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
150 dst.Set(src)
151 }
152 } else if src.Kind() == reflect.Ptr {
153 if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
154 return
155 }
156 } else if dst.Elem().Type() == src.Type() {
157 if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
158 return
159 }
160 } else {
161 return ErrDifferentArgumentsTypes
162 }
163 break
164 }
165 if dst.IsNil() || overwrite {
82166 if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
83167 dst.Set(src)
84168 }
85 } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
169 } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
86170 return
87171 }
88172 default:
97181 // src attributes if they themselves are not empty. dst and src must be valid same-type structs
98182 // and dst must be a pointer to struct.
99183 // It won't merge unexported (private) fields and will do recursively any exported field.
100 func Merge(dst, src interface{}) error {
101 return merge(dst, src, false)
184 func Merge(dst, src interface{}, opts ...func(*Config)) error {
185 return merge(dst, src, opts...)
102186 }
103187
104188 // MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
105189 // non-empty src attribute values.
106 func MergeWithOverwrite(dst, src interface{}) error {
107 return merge(dst, src, true)
108 }
109
110 func merge(dst, src interface{}, overwrite bool) error {
190 // Deprecated: use Merge(…) with WithOverride
191 func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
192 return merge(dst, src, append(opts, WithOverride)...)
193 }
194
195 // WithTransformers adds transformers to merge, allowing to customize the merging of some types.
196 func WithTransformers(transformers Transformers) func(*Config) {
197 return func(config *Config) {
198 config.Transformers = transformers
199 }
200 }
201
202 // WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
203 func WithOverride(config *Config) {
204 config.Overwrite = true
205 }
206
207 func merge(dst, src interface{}, opts ...func(*Config)) error {
111208 var (
112209 vDst, vSrc reflect.Value
113210 err error
114211 )
212
213 config := &Config{}
214
215 for _, opt := range opts {
216 opt(config)
217 }
218
115219 if vDst, vSrc, err = resolveValues(dst, src); err != nil {
116220 return err
117221 }
118222 if vDst.Type() != vSrc.Type() {
119223 return ErrDifferentArgumentsTypes
120224 }
121 return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
122 }
225 return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
226 }
0 package mergo
1
2 import (
3 "reflect"
4 "testing"
5 )
6
7 type transformer struct {
8 m map[reflect.Type]func(dst, src reflect.Value) error
9 }
10
11 func (s *transformer) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
12 if fn, ok := s.m[t]; ok {
13 return fn
14 }
15 return nil
16 }
17
18 type foo struct {
19 s string
20 Bar *bar
21 }
22
23 type bar struct {
24 i int
25 s map[string]string
26 }
27
28 func TestMergeWithTransformerNilStruct(t *testing.T) {
29 a := foo{s: "foo"}
30 b := foo{Bar: &bar{i: 2, s: map[string]string{"foo": "bar"}}}
31 if err := Merge(&a, &b, WithOverride, WithTransformers(&transformer{
32 m: map[reflect.Type]func(dst, src reflect.Value) error{
33 reflect.TypeOf(&bar{}): func(dst, src reflect.Value) error {
34 // Do sthg with Elem
35 t.Log(dst.Elem().FieldByName("i"))
36 t.Log(src.Elem())
37 return nil
38 },
39 },
40 })); err != nil {
41 t.Fatal(err)
42 }
43 if a.s != "foo" {
44 t.Fatalf("b not merged in properly: a.s.Value(%s) != expected(%s)", a.s, "foo")
45 }
46 if a.Bar == nil {
47 t.Fatalf("b not merged in properly: a.Bar shouldn't be nil")
48 }
49 }
3131 next *visit
3232 }
3333
34 // From src/pkg/encoding/json.
34 // From src/pkg/encoding/json/encode.go.
3535 func isEmptyValue(v reflect.Value) bool {
3636 switch v.Kind() {
3737 case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
4444 return v.Uint() == 0
4545 case reflect.Float32, reflect.Float64:
4646 return v.Float() == 0
47 case reflect.Interface, reflect.Ptr:
47 case reflect.Interface, reflect.Ptr, reflect.Func:
4848 return v.IsNil()
49 case reflect.Invalid:
50 return true
4951 }
5052 return false
5153 }
55 package mergo
66
77 import (
8 "gopkg.in/yaml.v2"
89 "io/ioutil"
910 "reflect"
1011 "testing"
1112 "time"
12
13 "gopkg.in/yaml.v1"
1413 )
1514
1615 type simpleTest struct {
2120 St simpleTest
2221 sz int
2322 ID string
23 }
24
25 type mapTest struct {
26 M map[int]int
27 }
28
29 type ifcTest struct {
30 I interface{}
2431 }
2532
2633 type moreComplextText struct {
217224 }
218225 }
219226
220 func TestSliceStruct(t *testing.T) {
221 a := sliceTest{}
222 b := sliceTest{[]int{1, 2, 3}}
227 func testSlice(t *testing.T, a []int, b []int) {
228 bc := b
229 e := append(a, b...)
230
231 sa := sliceTest{a}
232 sb := sliceTest{b}
233 if err := Merge(&sa, sb); err != nil {
234 t.FailNow()
235 }
236 if !reflect.DeepEqual(sb.S, bc) {
237 t.Fatalf("Source slice was modified %d != %d", sb.S, bc)
238 }
239 if !reflect.DeepEqual(sa.S, e) {
240 t.Fatalf("b not merged in a proper way %d != %d", sa.S, e)
241 }
242
243 ma := map[string][]int{"S": a}
244 mb := map[string][]int{"S": b}
245 if err := Merge(&ma, mb); err != nil {
246 t.FailNow()
247 }
248 if !reflect.DeepEqual(mb["S"], bc) {
249 t.Fatalf("Source slice was modified %d != %d", mb["S"], bc)
250 }
251 if !reflect.DeepEqual(ma["S"], e) {
252 t.Fatalf("b not merged in a proper way %d != %d", ma["S"], e)
253 }
254
255 if a == nil {
256 // test case with missing dst key
257 ma := map[string][]int{}
258 mb := map[string][]int{"S": b}
259 if err := Merge(&ma, mb); err != nil {
260 t.FailNow()
261 }
262 if !reflect.DeepEqual(mb["S"], bc) {
263 t.Fatalf("Source slice was modified %d != %d", mb["S"], bc)
264 }
265 if !reflect.DeepEqual(ma["S"], e) {
266 t.Fatalf("b not merged in a proper way %d != %d", ma["S"], e)
267 }
268 }
269
270 if b == nil {
271 // test case with missing src key
272 ma := map[string][]int{"S": a}
273 mb := map[string][]int{}
274 if err := Merge(&ma, mb); err != nil {
275 t.FailNow()
276 }
277 if !reflect.DeepEqual(mb["S"], bc) {
278 t.Fatalf("Source slice was modified %d != %d", mb["S"], bc)
279 }
280 if !reflect.DeepEqual(ma["S"], e) {
281 t.Fatalf("b not merged in a proper way %d != %d", ma["S"], e)
282 }
283 }
284 }
285
286 func TestSlice(t *testing.T) {
287 testSlice(t, nil, []int{1, 2, 3})
288 testSlice(t, []int{}, []int{1, 2, 3})
289 testSlice(t, []int{1}, []int{2, 3})
290 testSlice(t, []int{1}, []int{})
291 testSlice(t, []int{1}, nil)
292 }
293
294 func TestEmptyMaps(t *testing.T) {
295 a := mapTest{}
296 b := mapTest{
297 map[int]int{},
298 }
223299 if err := Merge(&a, b); err != nil {
224 t.FailNow()
225 }
226 if len(b.S) != 3 {
227 t.FailNow()
228 }
229 if len(a.S) != len(b.S) {
230 t.Fatalf("b not merged in a proper way %d != %d", len(a.S), len(b.S))
231 }
232
233 a = sliceTest{[]int{1}}
234 b = sliceTest{[]int{1, 2, 3}}
300 t.Fail()
301 }
302 if !reflect.DeepEqual(a, b) {
303 t.FailNow()
304 }
305 }
306
307 func TestEmptyToEmptyMaps(t *testing.T) {
308 a := mapTest{}
309 b := mapTest{}
235310 if err := Merge(&a, b); err != nil {
236 t.FailNow()
237 }
238 if len(a.S) != 1 {
239 t.FailNow()
240 }
241 if len(a.S) == len(b.S) {
242 t.Fatalf("b merged unexpectedly %d != %d", len(a.S), len(b.S))
311 t.Fail()
312 }
313 if !reflect.DeepEqual(a, b) {
314 t.FailNow()
315 }
316 }
317
318 func TestEmptyToNotEmptyMaps(t *testing.T) {
319 a := mapTest{map[int]int{
320 1: 2,
321 3: 4,
322 }}
323 aa := mapTest{map[int]int{
324 1: 2,
325 3: 4,
326 }}
327 b := mapTest{
328 map[int]int{},
329 }
330 if err := Merge(&a, b); err != nil {
331 t.Fail()
332 }
333 if !reflect.DeepEqual(a, aa) {
334 t.FailNow()
243335 }
244336 }
245337
312404 }
313405 }
314406
407 func TestMapsWithNilPointer(t *testing.T) {
408 m := map[string]*simpleTest{
409 "a": nil,
410 "b": nil,
411 }
412 n := map[string]*simpleTest{
413 "b": nil,
414 "c": nil,
415 }
416 expect := map[string]*simpleTest{
417 "a": nil,
418 "b": nil,
419 "c": nil,
420 }
421
422 if err := Merge(&m, n, WithOverride); err != nil {
423 t.Fatalf(err.Error())
424 }
425
426 if !reflect.DeepEqual(m, expect) {
427 t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
428 }
429 }
430
315431 func TestYAMLMaps(t *testing.T) {
316432 thing := loadYAML("testdata/thing.yml")
317433 license := loadYAML("testdata/license.yml")
318434 ft := thing["fields"].(map[interface{}]interface{})
319435 fl := license["fields"].(map[interface{}]interface{})
320 expectedLength := len(ft) + len(fl)
436 // license has one extra field (site) and another already existing in thing (author) that Mergo won't override.
437 expectedLength := len(ft) + len(fl) - 1
321438 if err := Merge(&license, thing); err != nil {
322439 t.Fatal(err.Error())
323440 }
392509 }
393510 }
394511
512 func TestIfcMap(t *testing.T) {
513 a := ifcTest{}
514 b := ifcTest{42}
515 if err := Map(&a, b); err != nil {
516 t.FailNow()
517 }
518 if a.I != 42 {
519 t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
520 }
521 if !reflect.DeepEqual(a, b) {
522 t.FailNow()
523 }
524 }
525
526 func TestIfcMapNoOverwrite(t *testing.T) {
527 a := ifcTest{13}
528 b := ifcTest{42}
529 if err := Map(&a, b); err != nil {
530 t.FailNow()
531 }
532 if a.I != 13 {
533 t.Fatalf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I)
534 }
535 }
536
537 func TestIfcMapWithOverwrite(t *testing.T) {
538 a := ifcTest{13}
539 b := ifcTest{42}
540 if err := MapWithOverwrite(&a, b); err != nil {
541 t.FailNow()
542 }
543 if a.I != 42 {
544 t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
545 }
546 if !reflect.DeepEqual(a, b) {
547 t.FailNow()
548 }
549 }
550
395551 type pointerMapTest struct {
396552 A int
397553 hidden int
433589 }
434590 }
435591
592 func TestEmbeddedPointerUnpacking(t *testing.T) {
593 tests := []struct{ input pointerMapTest }{
594 {pointerMapTest{42, 1, nil}},
595 {pointerMapTest{42, 1, &simpleTest{66}}},
596 }
597 newValue := 77
598 m := map[string]interface{}{
599 "b": map[string]interface{}{
600 "value": newValue,
601 },
602 }
603 for _, test := range tests {
604 pt := test.input
605 if err := MapWithOverwrite(&pt, m); err != nil {
606 t.FailNow()
607 }
608 if pt.B.Value != newValue {
609 t.Fatalf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue)
610 }
611
612 }
613 }
614
436615 type structWithTimePointer struct {
437616 Birth *time.Time
438617 }
510689
511690 func TestUnexportedProperty(t *testing.T) {
512691 a := structWithMap{map[string]structWithUnexportedProperty{
513 "key": structWithUnexportedProperty{"hello"},
692 "key": {"hello"},
514693 }}
515694 b := structWithMap{map[string]structWithUnexportedProperty{
516 "key": structWithUnexportedProperty{"hi"},
695 "key": {"hi"},
517696 }}
518697 defer func() {
519698 if r := recover(); r != nil {
522701 }()
523702 Merge(&a, b)
524703 }
704
705 type structWithBoolPointer struct {
706 C *bool
707 }
708
709 func TestBooleanPointer(t *testing.T) {
710 bt, bf := true, false
711 src := structWithBoolPointer{
712 &bt,
713 }
714 dst := structWithBoolPointer{
715 &bf,
716 }
717 if err := Merge(&dst, src); err != nil {
718 t.FailNow()
719 }
720 if dst.C == src.C {
721 t.Fatalf("dst.C should be a different pointer than src.C")
722 }
723 if *dst.C != *src.C {
724 t.Fatalf("dst.C should be true")
725 }
726 }
00 import: ../../../../fossene/db/schema/thing.yml
11 fields:
22 site: string
3 author: root
22 name: string
33 parent: ref "datu:thing"
44 status: enum(draft, public, private)
5 author: updater