functions: MergeFunc
This commit is contained in:
parent
aecd7b2e62
commit
30671d85ad
|
@ -634,6 +634,38 @@ var MatchkeysFunc = function.New(&function.Spec{
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// MergeFunc contructs a function that takes an arbitrary number of maps and
|
||||||
|
// returns a single map that contains a merged set of elements from all of the maps.
|
||||||
|
//
|
||||||
|
// If more than one given map defines the same key then the one that is later in
|
||||||
|
// the argument sequence takes precedence.
|
||||||
|
var MergeFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{},
|
||||||
|
VarParam: &function.Parameter{
|
||||||
|
Name: "maps",
|
||||||
|
Type: cty.DynamicPseudoType,
|
||||||
|
AllowUnknown: true,
|
||||||
|
AllowDynamicType: true,
|
||||||
|
AllowNull: true,
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.DynamicPseudoType),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
outputMap := make(map[string]cty.Value)
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
|
||||||
|
if !arg.Type().IsObjectType() && !arg.Type().IsMapType() {
|
||||||
|
return cty.NilVal, fmt.Errorf("arguments must be maps or objects, got %#v", arg.Type())
|
||||||
|
}
|
||||||
|
for it := arg.ElementIterator(); it.Next(); {
|
||||||
|
k, v := it.Element()
|
||||||
|
outputMap[k.AsString()] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cty.ObjectVal(outputMap), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// helper function to add an element to a list, if it does not already exist
|
// helper function to add an element to a list, if it does not already exist
|
||||||
func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) {
|
func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) {
|
||||||
for _, ele := range slice {
|
for _, ele := range slice {
|
||||||
|
@ -728,3 +760,12 @@ func Map(args ...cty.Value) (cty.Value, error) {
|
||||||
func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) {
|
func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) {
|
||||||
return MatchkeysFunc.Call([]cty.Value{values, keys, searchset})
|
return MatchkeysFunc.Call([]cty.Value{values, keys, searchset})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge takes an arbitrary number of maps and returns a single map that contains
|
||||||
|
// a merged set of elements from all of the maps.
|
||||||
|
//
|
||||||
|
// If more than one given map defines the same key then the one that is later in
|
||||||
|
// the argument sequence takes precedence.
|
||||||
|
func Merge(maps ...cty.Value) (cty.Value, error) {
|
||||||
|
return MergeFunc.Call(maps)
|
||||||
|
}
|
||||||
|
|
|
@ -1365,3 +1365,167 @@ func TestMatchkeys(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMerge(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Values []cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
[]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("b"),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"c": cty.StringVal("d"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("b"),
|
||||||
|
"c": cty.StringVal("d"),
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // merge with conflicts is ok, last in wins
|
||||||
|
[]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("b"),
|
||||||
|
"c": cty.StringVal("d"),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("x"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("x"),
|
||||||
|
"c": cty.StringVal("d"),
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // only accept maps
|
||||||
|
[]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.StringVal("b"),
|
||||||
|
"c": cty.StringVal("d"),
|
||||||
|
}),
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("a"),
|
||||||
|
cty.StringVal("x"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cty.NilVal,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // merge maps of maps
|
||||||
|
[]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.MapVal(map[string]cty.Value{
|
||||||
|
"b": cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"d": cty.MapVal(map[string]cty.Value{
|
||||||
|
"e": cty.StringVal("f"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.MapVal(map[string]cty.Value{
|
||||||
|
"b": cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
"d": cty.MapVal(map[string]cty.Value{
|
||||||
|
"e": cty.StringVal("f"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // map of lists
|
||||||
|
[]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("b"),
|
||||||
|
cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"d": cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("e"),
|
||||||
|
cty.StringVal("f"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("b"),
|
||||||
|
cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
"d": cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("e"),
|
||||||
|
cty.StringVal("f"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // merge map of various kinds
|
||||||
|
[]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("b"),
|
||||||
|
cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"d": cty.MapVal(map[string]cty.Value{
|
||||||
|
"e": cty.StringVal("f"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"a": cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("b"),
|
||||||
|
cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
"d": cty.MapVal(map[string]cty.Value{
|
||||||
|
"e": cty.StringVal("f"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // argument error: non map type
|
||||||
|
[]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"a": cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("b"),
|
||||||
|
cty.StringVal("c"),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
cty.ListVal([]cty.Value{
|
||||||
|
cty.StringVal("d"),
|
||||||
|
cty.StringVal("e"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
cty.NilVal,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("merge(%#v)", test.Values), func(t *testing.T) {
|
||||||
|
got, err := Merge(test.Values...)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"matchkeys": funcs.MatchkeysFunc,
|
"matchkeys": funcs.MatchkeysFunc,
|
||||||
"max": stdlib.MaxFunc,
|
"max": stdlib.MaxFunc,
|
||||||
"md5": funcs.Md5Func,
|
"md5": funcs.Md5Func,
|
||||||
"merge": unimplFunc, // TODO
|
"merge": funcs.MergeFunc,
|
||||||
"min": stdlib.MinFunc,
|
"min": stdlib.MinFunc,
|
||||||
"pathexpand": funcs.PathExpandFunc,
|
"pathexpand": funcs.PathExpandFunc,
|
||||||
"pow": funcs.PowFunc,
|
"pow": funcs.PowFunc,
|
||||||
|
|
Loading…
Reference in New Issue