Fix case-insensitively merging template maps (#15351)
This commit is contained in:
parent
784c554f71
commit
8958b6ecd7
3 changed files with 89 additions and 3 deletions
55
util/templates/merge.go
Normal file
55
util/templates/merge.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// https://github.com/peterbourgon/mergemap
|
||||
|
||||
const mergeMaxDepth = 100
|
||||
|
||||
var matchKey = strings.EqualFold
|
||||
|
||||
// mergeMaps recursively merges other into target using matchKey for key comparison
|
||||
func mergeMaps(other map[string]any, target map[string]any) error {
|
||||
// return mergo.Map(&target, other, mergo.WithOverride)
|
||||
// return util.DecodeOther(other, target)
|
||||
merge(target, other, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func merge(dst, src map[string]any, depth int) map[string]any {
|
||||
if depth > mergeMaxDepth {
|
||||
panic("too deep!")
|
||||
}
|
||||
for key, srcVal := range src {
|
||||
for k := range dst {
|
||||
if matchKey(k, key) {
|
||||
// overwrite key
|
||||
key = k
|
||||
|
||||
srcMap, srcMapOk := mapify(srcVal)
|
||||
dstMap, dstMapOk := mapify(dst[k])
|
||||
if srcMapOk && dstMapOk {
|
||||
srcVal = merge(dstMap, srcMap, depth+1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
dst[key] = srcVal
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func mapify(i any) (map[string]any, bool) {
|
||||
value := reflect.ValueOf(i)
|
||||
if value.Kind() == reflect.Map {
|
||||
m := map[string]any{}
|
||||
for _, k := range value.MapKeys() {
|
||||
m[k.String()] = value.MapIndex(k).Interface()
|
||||
}
|
||||
return m, true
|
||||
}
|
||||
return map[string]any{}, false
|
||||
}
|
||||
32
util/templates/merge_test.go
Normal file
32
util/templates/merge_test.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMergeMaps(t *testing.T) {
|
||||
target := map[string]any{
|
||||
"foo": "bar",
|
||||
"nested": map[string]any{
|
||||
"bar": "baz",
|
||||
},
|
||||
}
|
||||
other := map[string]any{
|
||||
"Foo": 1,
|
||||
"Nested": map[string]any{
|
||||
"Bar": 2,
|
||||
},
|
||||
"baz": 3,
|
||||
}
|
||||
|
||||
require.NoError(t, mergeMaps(other, target))
|
||||
require.Equal(t, map[string]any{
|
||||
"foo": 1,
|
||||
"nested": map[string]any{
|
||||
"bar": 2,
|
||||
},
|
||||
"baz": 3,
|
||||
}, target)
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/go-sprout/sprout"
|
||||
)
|
||||
|
||||
|
|
@ -267,9 +266,9 @@ func (t *Template) RenderProxyWithValues(values map[string]interface{}, lang str
|
|||
}
|
||||
|
||||
// RenderResult renders the result template to instantiate the proxy
|
||||
func (t *Template) RenderResult(renderMode int, other map[string]interface{}) ([]byte, map[string]interface{}, error) {
|
||||
func (t *Template) RenderResult(renderMode int, other map[string]any) ([]byte, map[string]any, error) {
|
||||
values := t.Defaults(renderMode)
|
||||
if err := util.DecodeOther(other, &values); err != nil {
|
||||
if err := mergeMaps(other, values); err != nil {
|
||||
return nil, values, err
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue