core: New interpolation function "chunklist"

This turns a list into a list of lists with each element (apart from possibly the last) being the given length.
This commit is contained in:
Gauthier Wallet 2017-10-10 20:56:13 +02:00 committed by Martin Atkins
parent 935890ec64
commit ed9ba576e3
3 changed files with 94 additions and 0 deletions

View File

@ -79,6 +79,7 @@ func Funcs() map[string]ast.Function {
"dirname": interpolationFuncDirname(),
"distinct": interpolationFuncDistinct(),
"element": interpolationFuncElement(),
"chunklist": interpolationFuncChunklist(),
"file": interpolationFuncFile(),
"matchkeys": interpolationFuncMatchKeys(),
"flatten": interpolationFuncFlatten(),
@ -1129,6 +1130,56 @@ func interpolationFuncElement() ast.Function {
}
}
// returns the `list` items chunked by `size`.
func interpolationFuncChunklist() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{
ast.TypeList, // inputList
ast.TypeInt, // size
},
ReturnType: ast.TypeList,
Callback: func(args []interface{}) (interface{}, error) {
output := make([]ast.Variable, 0)
values, _ := args[0].([]ast.Variable)
size, _ := args[1].(int)
// errors if size is negative
if size < 0 {
return nil, fmt.Errorf("The size argument must be positive")
}
// if size is 0, returns a list made of the initial list
if size == 0 {
output = append(output, ast.Variable{
Type: ast.TypeList,
Value: values,
})
return output, nil
}
variables := make([]ast.Variable, 0)
chunk := ast.Variable{
Type: ast.TypeList,
Value: variables,
}
l := len(values)
for i, v := range values {
variables = append(variables, v)
// Chunk when index isn't 0, or when reaching the values's length
if (i+1)%size == 0 || (i+1) == l {
chunk.Value = variables
output = append(output, chunk)
variables = make([]ast.Variable, 0)
}
}
return output, nil
},
}
}
// interpolationFuncKeys implements the "keys" function that yields a list of
// keys of map types within a Terraform configuration.
func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {

View File

@ -2106,6 +2106,44 @@ func TestInterpolateFuncElement(t *testing.T) {
})
}
func TestInterpolateFuncChunklist(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// normal usage
{
`${chunklist(list("a", "b", "c"), 1)}`,
[]interface{}{
[]interface{}{"a"},
[]interface{}{"b"},
[]interface{}{"c"},
},
false,
},
// `size` is pair and the list has an impair number of items
{
`${chunklist(list("a", "b", "c"), 2)}`,
[]interface{}{
[]interface{}{"a", "b"},
[]interface{}{"c"},
},
false,
},
// list made of the same list, since size is 0
{
`${chunklist(list("a", "b", "c"), 0)}`,
[]interface{}{[]interface{}{"a", "b", "c"}},
false,
},
// negative size of chunks
{
`${chunklist(list("a", "b", "c"), -1)}`,
nil,
true,
},
},
})
}
func TestInterpolateFuncBasename(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{

View File

@ -230,6 +230,11 @@ The supported built-in functions are:
* `element(aws_subnet.foo.*.id, count.index)`
* `element(var.list_of_strings, 2)`
* `chunklist(list, size)` - Returns the `list` items chunked by `size`.
Examples:
* `list(aws_subnet.foo.*.id, 1)`: will outputs `[["id1"], ["id2"], ["id3"]]`
* `list(var.list_of_strings, 2)`: will outputs `[["id1", "id2"], ["id3", "id4"], ["id5"]`
* `file(path)` - Reads the contents of a file into the string. Variables
in this file are _not_ interpolated. The contents of the file are
read as-is. The `path` is interpreted relative to the working directory.