Skip to content

Commit

Permalink
Merge branch 'master' into should-not-dereference
Browse files Browse the repository at this point in the history
  • Loading branch information
darccio committed Mar 16, 2023
2 parents 0e73161 + 62d1cf2 commit acd16ec
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 16 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/stackaid.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: 'fund-on-stackaid'
on:
push:
branches:
- master
jobs:
stackaid-json:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v3 # Only required for Go based repos
- uses: stackaid/generate-stackaid-json@v1.9
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Mergo


[![GoDoc][3]][4]
[![GitHub release][5]][6]
[![GoCard][7]][8]
Expand All @@ -9,6 +8,7 @@
[![Sourcegraph][11]][12]
[![FOSSA Status][13]][14]
[![Become my sponsor][15]][16]
[![Tidelift][17]][18]

[1]: https://travis-ci.org/imdario/mergo.png
[2]: https://travis-ci.org/imdario/mergo
Expand All @@ -26,6 +26,8 @@
[14]: https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield
[15]: https://img.shields.io/github/sponsors/imdario
[16]: https://github.com/sponsors/imdario
[17]: https://tidelift.com/badges/package/go/github.com%2Fimdario%2Fmergo
[18]: https://tidelift.com/subscription/pkg/go-github.com-imdario-mergo

A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.

Expand Down Expand Up @@ -55,7 +57,6 @@ If Mergo is useful to you, consider buying me a coffee, a beer, or making a mont

### Mergo in the wild

- [cli/cli](https://github.com/cli/cli)
- [moby/moby](https://github.com/moby/moby)
- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
- [vmware/dispatch](https://github.com/vmware/dispatch)
Expand Down
6 changes: 4 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
| 0.3.x | :white_check_mark: |
| < 0.3 | :x: |

## Reporting a Vulnerability
## Security contact information

Report any vulnerability to d@rio.hn.
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
138 changes: 138 additions & 0 deletions issue202_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package mergo_test

import (
"reflect"
"testing"

"github.com/imdario/mergo"
)

func TestIssue202(t *testing.T) {
tests := []struct {
name string
dst, src, want map[string]interface{}
}{
{
name: "slice override string",
dst: map[string]interface{}{
"x": 456,
"y": "foo",
},
src: map[string]interface{}{
"x": "123",
"y": []int{1, 2, 3},
},
want: map[string]interface{}{
"x": "123",
"y": []int{1, 2, 3},
},
},
{
name: "string override slice",
dst: map[string]interface{}{
"x": 456,
"y": []int{1, 2, 3},
},
src: map[string]interface{}{
"x": "123",
"y": "foo",
},
want: map[string]interface{}{
"x": "123",
"y": "foo",
},
},
{
name: "map override string",
dst: map[string]interface{}{
"x": 456,
"y": "foo",
},
src: map[string]interface{}{
"x": "123",
"y": map[string]interface{}{
"a": true,
},
},
want: map[string]interface{}{
"x": "123",
"y": map[string]interface{}{
"a": true,
},
},
},
{
name: "string override map",
dst: map[string]interface{}{
"x": 456,
"y": map[string]interface{}{
"a": true,
},
},
src: map[string]interface{}{
"x": "123",
"y": "foo",
},
want: map[string]interface{}{
"x": "123",
"y": "foo",
},
},
{
name: "map override map",
dst: map[string]interface{}{
"x": 456,
"y": map[string]interface{}{
"a": 10,
},
},
src: map[string]interface{}{
"x": "123",
"y": map[string]interface{}{
"a": true,
},
},
want: map[string]interface{}{
"x": "123",
"y": map[string]interface{}{
"a": true,
},
},
},
{
name: "map override map with merge",
dst: map[string]interface{}{
"x": 456,
"y": map[string]interface{}{
"a": 10,
"b": 100,
},
},
src: map[string]interface{}{
"x": "123",
"y": map[string]interface{}{
"a": true,
},
},
want: map[string]interface{}{
"x": "123",
"y": map[string]interface{}{
"a": true,
"b": 100,
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := mergo.Merge(&tt.dst, tt.src, mergo.WithOverride); err != nil {
t.Error(err)
}

if !reflect.DeepEqual(tt.dst, tt.want) {
t.Errorf("maps not equal.\nwant:\n%v\ngot:\n%v\n", tt.want, tt.dst)
}
})
}
}
33 changes: 33 additions & 0 deletions issue220_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package mergo_test

import (
"reflect"
"testing"

"github.com/imdario/mergo"
)

func TestIssue220(t *testing.T) {
dst := []interface{}{
map[string]int{
"a": 1,
},
}
src := []interface{}{
"nil",
}
expected := []interface{}{
map[string]int{
"a": 1,
},
}

err := mergo.Merge(&dst, src, mergo.WithSliceDeepCopy)
if err != nil {
t.Errorf("unexpected error %v", err)
}

if !reflect.DeepEqual(dst, expected) {
t.Errorf("expected: %#v\ngot: %#v", expected, dst)
}
}
85 changes: 85 additions & 0 deletions issue230_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package mergo_test

import (
"testing"

"github.com/imdario/mergo"
)

var testDataM = []struct {
M1 mapTest
M2 mapTest
WithOverrideEmptyValue bool
ExpectedMap map[int]int
}{
{
M1: mapTest{
M: map[int]int{1: 1, 3: 3},
},
M2: mapTest{
M: map[int]int{1: 2, 2: 2},
},
WithOverrideEmptyValue: true,
ExpectedMap: map[int]int{1: 1, 3: 3},
},
{
M1: mapTest{
M: map[int]int{1: 1, 3: 3},
},
M2: mapTest{
M: map[int]int{1: 2, 2: 2},
},
WithOverrideEmptyValue: false,
ExpectedMap: map[int]int{1: 1, 2: 2, 3: 3},
},
{
M1: mapTest{
M: map[int]int{},
},
M2: mapTest{
M: map[int]int{1: 2, 2: 2},
},
WithOverrideEmptyValue: true,
ExpectedMap: map[int]int{},
},
{
M1: mapTest{
M: map[int]int{},
},
M2: mapTest{
M: map[int]int{1: 2, 2: 2},
},
WithOverrideEmptyValue: false,
ExpectedMap: map[int]int{1: 2, 2: 2},
},
}

func withOverrideEmptyValue(enable bool) func(*mergo.Config) {
if enable {
return mergo.WithOverwriteWithEmptyValue
}

return mergo.WithOverride
}

func TestMergeMapWithOverride(t *testing.T) {
t.Parallel()

for _, data := range testDataM {
err := mergo.Merge(&data.M2, data.M1, withOverrideEmptyValue(data.WithOverrideEmptyValue))
if err != nil {
t.Errorf("Error while merging %s", err)
}

if len(data.M2.M) != len(data.ExpectedMap) {
t.Errorf("Got %d elements in map, but expected %d", len(data.M2.M), len(data.ExpectedMap))
return
}

for i, val := range data.M2.M {
if val != data.ExpectedMap[i] {
t.Errorf("Expected value: %d, but got %d while merging map", data.ExpectedMap[i], val)
}
}
}
}
4 changes: 0 additions & 4 deletions issue89_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ func TestIssue89MergeWithEmptyValue(t *testing.T) {
expected interface{}
key string
}{
{
3,
"A",
},
{
"",
"B",
Expand Down
2 changes: 1 addition & 1 deletion map.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {

func _map(dst, src interface{}, opts ...func(*Config)) error {
if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
return ErrNonPointerAgument
return ErrNonPointerArgument
}
var (
vDst, vSrc reflect.Value
Expand Down
24 changes: 20 additions & 4 deletions merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
}

if src.Kind() != reflect.Map {
if overwrite {
if overwrite && dst.CanSet() {
dst.Set(src)
}
return
Expand Down Expand Up @@ -195,8 +195,14 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
dst.SetMapIndex(key, dstSlice)
}
}
if dstElement.IsValid() && !isEmptyValue(dstElement, !config.ShouldNotDereference) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) {
continue

if dstElement.IsValid() && !isEmptyValue(dstElement, !config.ShouldNotDereference) {
if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice {
continue
}
if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map && reflect.TypeOf(dstElement.Interface()).Kind() == reflect.Map {
continue
}
}

if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement, !config.ShouldNotDereference)) {
Expand All @@ -206,6 +212,16 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
dst.SetMapIndex(key, srcElement)
}
}

// Ensure that all keys in dst are deleted if they are not in src.
if overwriteWithEmptySrc {
for _, key := range dst.MapKeys() {
srcElement := src.MapIndex(key)
if !srcElement.IsValid() {
dst.SetMapIndex(key, reflect.Value{})
}
}
}
case reflect.Slice:
if !dst.CanSet() {
break
Expand Down Expand Up @@ -357,7 +373,7 @@ func WithSliceDeepCopy(config *Config) {

func merge(dst, src interface{}, opts ...func(*Config)) error {
if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
return ErrNonPointerAgument
return ErrNonPointerArgument
}
var (
vDst, vSrc reflect.Value
Expand Down
Loading

0 comments on commit acd16ec

Please sign in to comment.