util/reflect: add ability to merge maps

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2024-09-20 19:22:20 +03:00
parent faf2454f0a
commit c21cd7c016
2 changed files with 57 additions and 0 deletions

View File

@ -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 // Merge merges map[string]interface{} to destination struct
func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error { func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error {
options := Options{} options := Options{}
@ -59,6 +90,11 @@ func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error {
return err return err
} }
if mapper, ok := dst.(map[string]interface{}); ok {
dst = mergeMap(mapper, mp, 0)
return nil
}
var err error var err error
var sval reflect.Value var sval reflect.Value
var fname string var fname string

View File

@ -4,6 +4,27 @@ import (
"testing" "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) { func TestFieldName(t *testing.T) {
src := "SomeVar" src := "SomeVar"
chk := "some_var" chk := "some_var"