Fix case-insensitively merging template maps (#15351)

This commit is contained in:
andig 2024-08-11 21:49:34 +02:00 • committed by GitHub
parent 784c554f71
commit 8958b6ecd7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 89 additions and 3 deletions

55
util/templates/merge.go Normal file
View 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
}

View 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)
}

View file

@ -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
}