diff --git a/lang/funcs/collection.go b/lang/funcs/collection.go index a806d1caf6..27641150cb 100644 --- a/lang/funcs/collection.go +++ b/lang/funcs/collection.go @@ -126,17 +126,15 @@ var CoalesceListFunc = function.New(&function.Spec{ argTypes := make([]cty.Type, len(args)) for i, arg := range args { - arg, err = convert.Convert(arg, cty.DynamicPseudoType) - if err != nil { - return cty.NilType, fmt.Errorf("all arguments must be lists or tuples") - } - argTypes[i] = arg.Type() } - fmt.Printf("%#v\n", argTypes) + retType, _ := convert.UnifyUnsafe(argTypes) + if retType == cty.NilType { + return cty.NilType, fmt.Errorf("all arguments must have the same type") + } - return cty.List(cty.DynamicPseudoType), nil + return retType, nil }, Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { @@ -161,6 +159,34 @@ var CoalesceListFunc = function.New(&function.Spec{ }, }) +// CompactFunc contructs a function that takes a list of strings and returns a new list +// with any empty string elements removed. +var CompactFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "list", + Type: cty.List(cty.String), + }, + }, + Type: function.StaticReturnType(cty.List(cty.String)), + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + + var outputList []cty.Value + + it := args[0].ElementIterator() + for it.Next() { + _, v := it.Element() + fmt.Println("ohai!") + fmt.Println(v.AsString()) + if v.AsString() == "" { + continue + } + outputList = append(outputList, v) + } + return cty.ListVal(outputList), nil + }, +}) + // Element returns a single element from a given list at the given index. If // index is greater than the length of the list then it is wrapped modulo // the list length. @@ -178,3 +204,9 @@ func Length(collection cty.Value) (cty.Value, error) { func CoalesceList(args ...cty.Value) (cty.Value, error) { return CoalesceListFunc.Call(args) } + +// Compact takes a list of strings and returns a new list +// with any empty string elements removed. +func Compact(list cty.Value) (cty.Value, error) { + return CoalesceListFunc.Call([]cty.Value{list}) +} diff --git a/lang/funcs/collection_test.go b/lang/funcs/collection_test.go index b30709a3c2..0385e4c64a 100644 --- a/lang/funcs/collection_test.go +++ b/lang/funcs/collection_test.go @@ -269,7 +269,7 @@ func TestCoalesceList(t *testing.T) { }), false, }, - { // lists with value type mismatch + { // lists with mixed types []cty.Value{ cty.ListVal([]cty.Value{ cty.StringVal("first"), cty.StringVal("second"), @@ -279,20 +279,23 @@ func TestCoalesceList(t *testing.T) { cty.NumberIntVal(2), }), }, - cty.NilVal, - true, + cty.ListVal([]cty.Value{ + cty.StringVal("first"), cty.StringVal("second"), + }), + false, }, - { // mixed list and tuple + { // lists with mixed types []cty.Value{ + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + }), cty.ListVal([]cty.Value{ cty.StringVal("first"), cty.StringVal("second"), }), - cty.TupleVal([]cty.Value{ - cty.StringVal("third"), - }), }, cty.ListVal([]cty.Value{ - cty.StringVal("first"), cty.StringVal("second"), + cty.StringVal("1"), cty.StringVal("2"), }), false, }, @@ -317,3 +320,43 @@ func TestCoalesceList(t *testing.T) { }) } } + +func TestCompact(t *testing.T) { + tests := []struct { + List cty.Value + Want cty.Value + Err bool + }{ + { + cty.ListVal([]cty.Value{ + cty.StringVal("test"), + cty.StringVal(""), + cty.StringVal("test"), + }), + cty.ListVal([]cty.Value{ + cty.StringVal("test"), + cty.StringVal("test"), + }), + false, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("compact(%#v)", test.List), func(t *testing.T) { + got, err := Compact(test.List) + + 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) + } + }) + } +}