better merge of values

This commit is contained in:
Mikaël Cluseau
2019-10-19 15:08:41 +11:00
parent 840824d438
commit fec03e0a7e
5 changed files with 275 additions and 416 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"log"
"path"
"reflect"
yaml "gopkg.in/yaml.v2"
@ -62,15 +63,44 @@ func newRenderContext(host *clustersconfig.Host, cfg *clustersconfig.Config) (ct
func mapMerge(target, source map[string]interface{}) {
for k, v := range source {
if tMap, targetIsMap := target[k].(map[string]interface{}); targetIsMap {
if sMap, sourceIsMap := v.(map[string]interface{}); sourceIsMap {
mapMerge(tMap, sMap)
target[k] = genericMerge(target[k], v)
}
}
func genericMerge(target, source interface{}) (result interface{}) {
srcV := reflect.ValueOf(source)
tgtV := reflect.ValueOf(target)
if srcV.Kind() == reflect.Map && tgtV.Kind() == reflect.Map {
// XXX maybe more specific later
result = map[interface{}]interface{}{}
resultV := reflect.ValueOf(result)
tgtIt := tgtV.MapRange()
for tgtIt.Next() {
sv := srcV.MapIndex(tgtIt.Key())
if sv.Kind() == 0 {
resultV.SetMapIndex(tgtIt.Key(), tgtIt.Value())
continue
}
merged := genericMerge(tgtIt.Value().Interface(), sv.Interface())
resultV.SetMapIndex(tgtIt.Key(), reflect.ValueOf(merged))
}
target[k] = v
srcIt := srcV.MapRange()
for srcIt.Next() {
if resultV.MapIndex(srcIt.Key()).Kind() != 0 {
continue // already done
}
resultV.SetMapIndex(srcIt.Key(), srcIt.Value())
}
return
}
return source
}
func (ctx *renderContext) Config() string {

View File

@ -0,0 +1,54 @@
package main
import (
"testing"
yaml "gopkg.in/yaml.v2"
)
func TestMerge(t *testing.T) {
if v := genericMerge("a", "b"); v != "b" {
t.Errorf("got %q", v)
}
if v := unparse(genericMerge(parse(`
a: t
b: t
m:
a1: t
b1: t
`), parse(`
a: s
c: s
m:
a1: s
c1: s
`))); "\n"+v != `
a: s
b: t
c: s
m:
a1: s
b1: t
c1: s
` {
t.Errorf("got\n%s", v)
}
}
func parse(s string) (r interface{}) {
r = map[string]interface{}{}
err := yaml.Unmarshal([]byte(s), r)
if err != nil {
panic(err)
}
return
}
func unparse(v interface{}) (s string) {
ba, err := yaml.Marshal(v)
if err != nil {
panic(err)
}
return string(ba)
}