diff --git a/util/reflect/reflect.go b/util/reflect/reflect.go index f0cda32c..729b4e02 100644 --- a/util/reflect/reflect.go +++ b/util/reflect/reflect.go @@ -44,6 +44,37 @@ func SliceAppend(b bool) Option { } } +var maxDepth = 32 + +func mergeMap(dst, src map[string]interface{}, depth int) map[string]interface{} { + if depth > maxDepth { + return dst + } + for key, srcVal := range src { + if dstVal, ok := dst[key]; ok { + srcMap, srcMapOk := mapify(srcVal) + dstMap, dstMapOk := mapify(dstVal) + if srcMapOk && dstMapOk { + srcVal = mergeMap(dstMap, srcMap, depth+1) + } + } + dst[key] = srcVal + } + return dst +} + +func mapify(i interface{}) (map[string]interface{}, bool) { + value := reflect.ValueOf(i) + if value.Kind() == reflect.Map { + m := map[string]interface{}{} + for _, k := range value.MapKeys() { + m[k.String()] = value.MapIndex(k).Interface() + } + return m, true + } + return map[string]interface{}{}, false +} + // Merge merges map[string]interface{} to destination struct func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error { options := Options{} @@ -59,6 +90,11 @@ func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error { return err } + if mapper, ok := dst.(map[string]interface{}); ok { + dst = mergeMap(mapper, mp, 0) + return nil + } + var err error var sval reflect.Value var fname string diff --git a/util/reflect/reflect_test.go b/util/reflect/reflect_test.go index a2cef0de..c11d7736 100644 --- a/util/reflect/reflect_test.go +++ b/util/reflect/reflect_test.go @@ -4,6 +4,27 @@ import ( "testing" ) +func TestMergeMap(t *testing.T) { + src := map[string]interface{}{ + "skey1": "sval1", + "skey2": map[string]interface{}{ + "skey3": "sval3", + }, + } + dst := map[string]interface{}{ + "skey1": "dval1", + "skey2": map[string]interface{}{ + "skey3": "dval3", + }, + } + + if err := Merge(src, dst); err != nil { + t.Fatal(err) + } + + t.Logf("%#+v", src) +} + func TestFieldName(t *testing.T) { src := "SomeVar" chk := "some_var"