From 30a924e162af925d63fc6c3c88a79eba2fb2033d Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Tue, 4 Jun 2019 08:36:30 -0400 Subject: [PATCH] lang/funcs: `matchkeys` - unify type for keys and searchset Added higher-level test for matchkeys to exercise mixing types in searchset. This had to be in the functions tests so the HCL auto conversion from tuple to list would occur. --- lang/funcs/collection.go | 19 +++++++++++++++---- lang/funcs/collection_test.go | 31 +++++++++++++++++++++++++++++++ lang/functions_test.go | 7 +++++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/lang/funcs/collection.go b/lang/funcs/collection.go index c6adce52de..232dd4258d 100644 --- a/lang/funcs/collection.go +++ b/lang/funcs/collection.go @@ -800,6 +800,8 @@ var MatchkeysFunc = function.New(&function.Spec{ }, }, Type: func(args []cty.Value) (cty.Type, error) { + // Verify that args[1] (keys) and args[2] (searchset) are of the same + // (or unifiable) type argTypes := make([]cty.Type, 2) for i := 0; i < 2; i++ { argTypes[i] = args[i+1].Type() @@ -807,9 +809,10 @@ var MatchkeysFunc = function.New(&function.Spec{ ty, _ := convert.UnifyUnsafe(argTypes) if ty == cty.NilType { - return cty.NilType, errors.New("lists must be of the same type") + return cty.NilType, errors.New("keys and searchset must be of the same type") } + // the return type is based on args[0] (values) return args[0].Type(), nil }, Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { @@ -822,10 +825,18 @@ var MatchkeysFunc = function.New(&function.Spec{ } output := make([]cty.Value, 0) - values := args[0] - keys := args[1] - searchset := args[2] + + // Keys and searchset must be the same type. + // We can skip error checking here because we've already verified that + // they can be unified in the Type function + argTypes := make([]cty.Type, 2) + for i := 0; i < 2; i++ { + argTypes[i] = args[i+1].Type() + } + ty, _ := convert.UnifyUnsafe(argTypes) + keys, _ := convert.Convert(args[1], ty) + searchset, _ := convert.Convert(args[2], ty) // if searchset is empty, return an empty list. if searchset.LengthInt() == 0 { diff --git a/lang/funcs/collection_test.go b/lang/funcs/collection_test.go index bfc007c8c8..581e212ff7 100644 --- a/lang/funcs/collection_test.go +++ b/lang/funcs/collection_test.go @@ -1896,6 +1896,37 @@ func TestMatchkeys(t *testing.T) { cty.ListValEmpty(cty.String), false, }, + { // complex values: values is a different type from keys and searchset + cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + cty.MapVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + }), + cty.MapVal(map[string]cty.Value{ + "foo": cty.StringVal("beep"), + }), + }), + cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("b"), + cty.StringVal("c"), + }), + cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("c"), + }), + cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + cty.MapVal(map[string]cty.Value{ + "foo": cty.StringVal("beep"), + }), + }), + false, + }, // errors { // different types cty.ListVal([]cty.Value{ diff --git a/lang/functions_test.go b/lang/functions_test.go index 5fdca59d35..8717ec7cd6 100644 --- a/lang/functions_test.go +++ b/lang/functions_test.go @@ -459,6 +459,13 @@ func TestFunctions(t *testing.T) { cty.StringVal("a"), }), }, + { // mixing types in searchset + `matchkeys(["a", "b", "c"], [1, 2, 3], [1, "3"])`, + cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("c"), + }), + }, }, "max": {