diff --git a/config/config_test.go b/config/config_test.go index 0937e3cb..d382249e 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -10,55 +10,6 @@ import ( "strings" ) -func TestNilMap(t *testing.T) { - assert := require.New(t) - var m map[string]interface{} = nil - assert.True(m == nil) -} - -func TestMapCopy(t *testing.T) { - assert := require.New(t) - m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"} - m1 := util.MapCopy(m0) - - delete(m0, "a") - assert.Equal(len(m1), len(m0)+1) - - b0 := m0["b"].(map[interface{}]interface{}) - b1 := m1["b"].(map[interface{}]interface{}) - b1["e"] = "queer" - - assert.Equal(len(b1), len(b0)+1) -} - -func TestMapsIntersection(t *testing.T) { - assert := require.New(t) - - m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"} - m1 := util.MapCopy(m0) - - delete(m0, "a") - b1 := m1["b"].(map[interface{}]interface{}) - delete(b1, "c") - expected := map[interface{}]interface{}{"b": map[interface{}]interface{}{}, "d": "4"} - assert.Equal(expected, util.MapsIntersection(m0, m1, util.Equal)) -} - -func TestMapsUnion(t *testing.T) { - assert := require.New(t) - - m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"} - m1 := util.MapCopy(m0) - m1["e"] = "added" - m1["d"] = "replaced" - - delete(m0, "a") - b1 := m1["b"].(map[interface{}]interface{}) - delete(b1, "c") - expected := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "replaced", "e": "added"} - assert.Equal(expected, util.MapsUnion(m0, m1, util.Replace)) -} - func TestFilterKey(t *testing.T) { assert := require.New(t) data := map[interface{}]interface{}{ diff --git a/util/util.go b/util/util.go index 6eb8e7d8..7b484c85 100644 --- a/util/util.go +++ b/util/util.go @@ -181,6 +181,8 @@ func Copy(d interface{}) interface{} { switch d := d.(type) { case map[interface{}]interface{}: return MapCopy(d) + case []interface{}: + return SliceCopy(d) default: return d } @@ -197,11 +199,40 @@ func Equal(l, r interface{}) interface{} { return nil } +func ExistsIn(x interface{}, s []interface{}) bool { + for _, y := range s { + if reflect.DeepEqual(x, y) { + return true + } + } + return false +} + +func SlicesUnion(left, right []interface{}, op func(interface{}, interface{}) interface{}) []interface{} { + result := SliceCopy(left) + for _, r := range right { + if !ExistsIn(r, result) { + result = append(result, r) + } + } + return result +} + +func SlicesIntersection(left, right []interface{}, op func(interface{}, interface{}) interface{}) []interface{} { + result := []interface{}{} + for _, r := range right { + if ExistsIn(r, left) { + result = append(result, r) + } + } + return result +} + func MapsUnion(left, right map[interface{}]interface{}, op func(interface{}, interface{}) interface{}) map[interface{}]interface{} { result := MapCopy(left) for k, r := range right { - if l, ok := result[k]; ok { + if l, ok := left[k]; ok { switch l := l.(type) { case map[interface{}]interface{}: switch r := r.(type) { @@ -210,6 +241,13 @@ func MapsUnion(left, right map[interface{}]interface{}, op func(interface{}, int default: result[k] = op(l, r) } + case []interface{}: + switch r := r.(type) { + case []interface{}: + result[k] = SlicesUnion(l, r, op) + default: + result[k] = op(l, r) + } default: result[k] = op(l, r) } @@ -226,19 +264,28 @@ func MapsIntersection(left, right map[interface{}]interface{}, op func(interface for k, l := range left { if r, ok := right[k]; ok { - switch r := r.(type) { + switch l := l.(type) { case map[interface{}]interface{}: - switch l := l.(type) { + switch r := r.(type) { case map[interface{}]interface{}: result[k] = MapsIntersection(l, r, op) default: - if i := op(l, r); i != nil { - result[k] = i + if v := op(l, r); v != nil { + result[k] = v + } + } + case []interface{}: + switch r := r.(type) { + case []interface{}: + result[k] = SlicesIntersection(l, r, op) + default: + if v := op(l, r); v != nil { + result[k] = v } } default: - if i := op(l, r); i != nil { - result[k] = i + if v := op(l, r); v != nil { + result[k] = v } } } @@ -255,6 +302,14 @@ func MapCopy(data map[interface{}]interface{}) map[interface{}]interface{} { return result } +func SliceCopy(data []interface{}) []interface{} { + result := make([]interface{}, len(data), len(data)) + for k, v := range data { + result[k] = Copy(v) + } + return result +} + func GetServices(urls []string) ([]string, error) { result := []string{} diff --git a/util/util_test.go b/util/util_test.go index a2a06fdc..000ab0ec 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -6,6 +6,90 @@ import ( "testing" ) +func TestNilMap(t *testing.T) { + assert := require.New(t) + var m map[string]interface{} = nil + assert.True(m == nil) +} + +func TestMapCopy(t *testing.T) { + assert := require.New(t) + m0 := map[interface{}]interface{}{"a": 1, "b": map[interface{}]interface{}{"c": 3}, "d": "4"} + m1 := MapCopy(m0) + assert.Equal(m0, m1) + + delete(m0, "a") + assert.Equal(len(m1), len(m0)+1) + + b0 := m0["b"].(map[interface{}]interface{}) + b1 := m1["b"].(map[interface{}]interface{}) + b1["e"] = "queer" + + assert.Equal(len(b1), len(b0)+1) +} + +func TestSliceCopy(t *testing.T) { + assert := require.New(t) + m0 := []interface{}{1, map[interface{}]interface{}{"c": 3}, "4"} + m1 := SliceCopy(m0) + assert.Equal(m0, m1) + + m0 = m0[1:] + assert.Equal(len(m1), len(m0)+1) + + b0 := m0[0].(map[interface{}]interface{}) + b1 := m1[1].(map[interface{}]interface{}) + b1["e"] = "queer" + + assert.Equal(len(b1), len(b0)+1) +} + +func TestMapsIntersection(t *testing.T) { + assert := require.New(t) + + m0 := map[interface{}]interface{}{ + "a": 1, + "b": map[interface{}]interface{}{"c": 3}, + "d": "4", + "e": []interface{}{1, 2, 3}, + } + m1 := MapCopy(m0) + + delete(m0, "a") + b1 := m1["b"].(map[interface{}]interface{}) + delete(b1, "c") + m1["e"] = []interface{}{2, 3, 4} + expected := map[interface{}]interface{}{"b": map[interface{}]interface{}{}, "d": "4", "e": []interface{}{2, 3}} + assert.Equal(expected, MapsIntersection(m0, m1, Equal)) +} + +func TestMapsUnion(t *testing.T) { + assert := require.New(t) + + m0 := map[interface{}]interface{}{ + "a": 1, + "b": map[interface{}]interface{}{"c": 3}, + "d": "4", + "f": []interface{}{1, 2, 3}, + } + m1 := MapCopy(m0) + m1["e"] = "added" + m1["d"] = "replaced" + m1["f"] = []interface{}{2, 3, 4} + + delete(m0, "a") + b1 := m1["b"].(map[interface{}]interface{}) + delete(b1, "c") + expected := map[interface{}]interface{}{ + "a": 1, + "b": map[interface{}]interface{}{"c": 3}, + "d": "replaced", + "e": "added", + "f": []interface{}{1, 2, 3, 4}, + } + assert.Equal(expected, MapsUnion(m0, m1, Replace)) +} + func TestLoadResourceSimple(t *testing.T) { assert := require.New(t)