mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
Add concat to accept lists of lists and maps
This will allow the concat interpolation function to accept lists of lists, and lists of maps as well as strings. We still allow bare strings for backwards compatibility, but remove some of the old comment wording as it could cause confusion of this function with actual string concatenation. Since maps are now supported in the config, this removes the superfluous (and failing) TestInterpolationFuncConcatListOfMaps.
This commit is contained in:
parent
2bd7cfd5fe
commit
8dcbc0b0a0
@ -258,10 +258,8 @@ func interpolationFuncCoalesce() ast.Function {
|
||||
}
|
||||
}
|
||||
|
||||
// interpolationFuncConcat implements the "concat" function that
|
||||
// concatenates multiple strings. This isn't actually necessary anymore
|
||||
// since our language supports string concat natively, but for backwards
|
||||
// compat we do this.
|
||||
// interpolationFuncConcat implements the "concat" function that concatenates
|
||||
// multiple lists.
|
||||
func interpolationFuncConcat() ast.Function {
|
||||
return ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeAny},
|
||||
@ -269,33 +267,42 @@ func interpolationFuncConcat() ast.Function {
|
||||
Variadic: true,
|
||||
VariadicType: ast.TypeAny,
|
||||
Callback: func(args []interface{}) (interface{}, error) {
|
||||
var finalListElements []string
|
||||
var outputList []ast.Variable
|
||||
|
||||
for _, arg := range args {
|
||||
// Append strings for backward compatibility
|
||||
if argument, ok := arg.(string); ok {
|
||||
finalListElements = append(finalListElements, argument)
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise variables
|
||||
if argument, ok := arg.([]ast.Variable); ok {
|
||||
for _, element := range argument {
|
||||
t := element.Type
|
||||
switch t {
|
||||
switch arg := arg.(type) {
|
||||
case string:
|
||||
outputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: arg})
|
||||
case []ast.Variable:
|
||||
for _, v := range arg {
|
||||
switch v.Type {
|
||||
case ast.TypeString:
|
||||
finalListElements = append(finalListElements, element.Value.(string))
|
||||
outputList = append(outputList, v)
|
||||
case ast.TypeList:
|
||||
outputList = append(outputList, v)
|
||||
case ast.TypeMap:
|
||||
outputList = append(outputList, v)
|
||||
default:
|
||||
return nil, fmt.Errorf("concat() does not support lists of %s", t.Printable())
|
||||
return nil, fmt.Errorf("concat() does not support lists of %s", v.Type.Printable())
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("arguments to concat() must be a string or list of strings")
|
||||
default:
|
||||
return nil, fmt.Errorf("concat() does not support %T", arg)
|
||||
}
|
||||
}
|
||||
|
||||
return stringSliceToVariableValue(finalListElements), nil
|
||||
// we don't support heterogeneous types, so make sure all types match the first
|
||||
if len(outputList) > 0 {
|
||||
firstType := outputList[0].Type
|
||||
for _, v := range outputList[1:] {
|
||||
if v.Type != firstType {
|
||||
return nil, fmt.Errorf("unexpected %s in list of %s", v.Type.Printable(), firstType.Printable())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outputList, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hil"
|
||||
@ -325,44 +324,88 @@ func TestInterpolateFuncConcat(t *testing.T) {
|
||||
[]interface{}{"a", "b", "c", "d", "e", "f", "0", "1"},
|
||||
false,
|
||||
},
|
||||
|
||||
// list vars
|
||||
{
|
||||
`${concat("${var.list}", "${var.list}")}`,
|
||||
[]interface{}{"a", "b", "a", "b"},
|
||||
false,
|
||||
},
|
||||
// lists of lists
|
||||
{
|
||||
`${concat("${var.lists}", "${var.lists}")}`,
|
||||
[]interface{}{[]interface{}{"c", "d"}, []interface{}{"c", "d"}},
|
||||
false,
|
||||
},
|
||||
|
||||
// lists of maps
|
||||
{
|
||||
`${concat("${var.maps}", "${var.maps}")}`,
|
||||
[]interface{}{map[string]interface{}{"key1": "a", "key2": "b"}, map[string]interface{}{"key1": "a", "key2": "b"}},
|
||||
false,
|
||||
},
|
||||
|
||||
// mismatched types
|
||||
{
|
||||
`${concat("${var.lists}", "${var.maps}")}`,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
},
|
||||
Vars: map[string]ast.Variable{
|
||||
"var.list": {
|
||||
Type: ast.TypeList,
|
||||
Value: []ast.Variable{
|
||||
{
|
||||
Type: ast.TypeString,
|
||||
Value: "a",
|
||||
},
|
||||
{
|
||||
Type: ast.TypeString,
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
},
|
||||
"var.lists": {
|
||||
Type: ast.TypeList,
|
||||
Value: []ast.Variable{
|
||||
{
|
||||
Type: ast.TypeList,
|
||||
Value: []ast.Variable{
|
||||
{
|
||||
Type: ast.TypeString,
|
||||
Value: "c",
|
||||
},
|
||||
{
|
||||
Type: ast.TypeString,
|
||||
Value: "d",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"var.maps": {
|
||||
Type: ast.TypeList,
|
||||
Value: []ast.Variable{
|
||||
{
|
||||
Type: ast.TypeMap,
|
||||
Value: map[string]ast.Variable{
|
||||
"key1": {
|
||||
Type: ast.TypeString,
|
||||
Value: "a",
|
||||
},
|
||||
"key2": {
|
||||
Type: ast.TypeString,
|
||||
Value: "b",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: This test is split out and calls a private function
|
||||
// because there's no good way to get a list of maps into the unit
|
||||
// tests due to GH-7142 - once lists of maps can be expressed properly as
|
||||
// literals this unit test can be wrapped back into the suite above.
|
||||
//
|
||||
// Reproduces crash reported in GH-7030.
|
||||
func TestInterpolationFuncConcatListOfMaps(t *testing.T) {
|
||||
listOfMapsOne := ast.Variable{
|
||||
Type: ast.TypeList,
|
||||
Value: []ast.Variable{
|
||||
{
|
||||
Type: ast.TypeMap,
|
||||
Value: map[string]interface{}{"one": "foo"},
|
||||
},
|
||||
},
|
||||
}
|
||||
listOfMapsTwo := ast.Variable{
|
||||
Type: ast.TypeList,
|
||||
Value: []ast.Variable{
|
||||
{
|
||||
Type: ast.TypeMap,
|
||||
Value: map[string]interface{}{"two": "bar"},
|
||||
},
|
||||
},
|
||||
}
|
||||
args := []interface{}{listOfMapsOne.Value, listOfMapsTwo.Value}
|
||||
|
||||
_, err := interpolationFuncConcat().Callback(args)
|
||||
|
||||
if err == nil || !strings.Contains(err.Error(), "concat() does not support lists of type map") {
|
||||
t.Fatalf("Expected err, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpolateFuncDistinct(t *testing.T) {
|
||||
testFunction(t, testFunctionConfig{
|
||||
Cases: []testFunctionCase{
|
||||
|
Loading…
Reference in New Issue
Block a user