From 917be5d8beb6f7f93eae70f07994d813139798dc Mon Sep 17 00:00:00 2001 From: Martin Magr Date: Wed, 24 Mar 2021 15:38:25 +0100 Subject: [PATCH] Move map operations from tools These two operations are used on multiple places in our tools, so it make sence to have it in the library. --- misc/structs.go | 67 ++++++++++++++++++++++++++++++++ tests/misc_test.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 misc/structs.go create mode 100644 tests/misc_test.go diff --git a/misc/structs.go b/misc/structs.go new file mode 100644 index 0000000..de8af51 --- /dev/null +++ b/misc/structs.go @@ -0,0 +1,67 @@ +package misc + +import ( + "fmt" + "log" + "strings" +) + +// AssimilateMap recursively saves content of the given map to destination map of strings +func AssimilateMap(theMap map[string]interface{}, destination *map[string]string) { + defer func() { // recover from any panic + if r := recover(); r != nil { + log.Printf("Panic:recovered in assimilateMap %v\n", r) + } + }() + for key, val := range theMap { + switch value := val.(type) { + case map[string]interface{}: + // go one level deeper in the map + AssimilateMap(value, destination) + case []interface{}: + // transform slice value to comma separated list and assimilate it + aList := make([]string, 0, len(value)) + for _, item := range value { + if itm, ok := item.(string); ok { + aList = append(aList, itm) + } + } + (*destination)[key] = strings.Join(aList, ",") + case float64, float32: + (*destination)[key] = fmt.Sprintf("%f", value) + case int, int8, int16, int32, int64: + (*destination)[key] = fmt.Sprintf("%d", value) + case bool: + (*destination)[key] = fmt.Sprintf("%t", value) + default: + // assimilate KV pair + if stringer, ok := value.(fmt.Stringer); ok { + (*destination)[key] = stringer.String() + } else { + (*destination)[key] = value.(string) + } + } + } +} + +// MergeMaps merges given maps into a new one +func MergeMaps(ms ...map[string]interface{}) map[string]interface{} { + res := make(map[string]interface{}) + for _, m := range ms { + for k, v := range m { + switch value := v.(type) { + case map[string]interface{}: + // go one level deeper in the map + res[k] = MergeMaps(value) + case []interface{}: + vCopy := make([]interface{}, len(value)) + copy(vCopy, value) + res[k] = vCopy + default: + // assimilate KV pair + res[k] = value + } + } + } + return res +} diff --git a/tests/misc_test.go b/tests/misc_test.go new file mode 100644 index 0000000..20e0255 --- /dev/null +++ b/tests/misc_test.go @@ -0,0 +1,95 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/infrawatch/apputils/misc" + "github.com/stretchr/testify/assert" +) + +type TestStringer struct { + numvalue int + strvalue string +} + +func (ts TestStringer) String() string { + return fmt.Sprintf("%s %d!", ts.strvalue, ts.numvalue) +} + +func TestStructsOperations(t *testing.T) { + map1 := map[string]interface{}{ + "map": map[string]interface{}{ + "a": "A", + "b": "B", + "c": map[string]interface{}{ + "1": 1, + "2": 2, + }, + "d": []interface{}{"d1", "d2"}, + }, + "slice": []interface{}{ + "A", + "B", + }, + "float32": float32(.32), + "float64": float64(.64), + "int": int(1), + "int8": int8(2), + "int16": int16(3), + "int32": int32(4), + "int64": int64(5), + "bool": true, + "string": "dub dub", + "stringer": TestStringer{numvalue: 666, strvalue: "wubba lubba"}, + } + map2 := map[string]string{"key": "value"} + map3 := map[string]interface{}{ + "map": map[string]interface{}{"foo": "bar"}, + "slice": []interface{}{"wubba", "lubba", "dub dub"}, + } + + t.Run("Test AssimilateMap", func(t *testing.T) { + testAssimilate := map[string]string{ + "key": "value", + "a": "A", + "b": "B", + "1": "1", + "2": "2", + "d": "d1,d2", + "slice": "A,B", + "float32": "0.320000", + "float64": "0.640000", + "int": "1", + "int8": "2", + "int16": "3", + "int32": "4", + "int64": "5", + "bool": "true", + "string": "dub dub", + "stringer": "wubba lubba 666!", + } + misc.AssimilateMap(map1, &map2) + assert.Equal(t, testAssimilate, map2) + }) + + t.Run("Test MergeMaps", func(t *testing.T) { + testMerge := map[string]interface{}{ + "float32": float32(.32), + "float64": float64(.64), + "int": int(1), + "int8": int8(2), + "int16": int16(3), + "int32": int32(4), + "int64": int64(5), + "bool": true, + "string": "dub dub", + "stringer": TestStringer{numvalue: 666, strvalue: "wubba lubba"}, + "map": map[string]interface{}{"foo": "bar"}, + "slice": []interface{}{"wubba", "lubba", "dub dub"}, + } + map4 := misc.MergeMaps(map1, map3) + assert.Equal(t, testMerge, map4) + }) + +}