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
|
||||
func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) {
|
||||
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) {
|
||||
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,
|
||||
"max": stdlib.MaxFunc,
|
||||
"md5": funcs.Md5Func,
|
||||
"merge": unimplFunc, // TODO
|
||||
"merge": funcs.MergeFunc,
|
||||
"min": stdlib.MinFunc,
|
||||
"pathexpand": funcs.PathExpandFunc,
|
||||
"pow": funcs.PowFunc,
|
||||
|
|
Loading…
Reference in New Issue