core: Add list() interpolation function

The list() interpolation function provides a way to add support for list
literals (of strings) to HIL without having to invent new syntax for it
and modify the HIL parser.

It presents as a function, thus:

    - list() -> []
    - list("a") -> ["a"]
    - list("a", "b") -> ["a", "b"]

Thanks to @wr0ngway for the idea of this approach, fixes #7460.
This commit is contained in:
James Nugent 2016-07-07 13:19:41 +01:00 committed by James Bardin
parent 1cf1b5c9d6
commit 58dd41f3b1
3 changed files with 75 additions and 0 deletions

View File

@ -69,6 +69,7 @@ func Funcs() map[string]ast.Function {
"join": interpolationFuncJoin(), "join": interpolationFuncJoin(),
"jsonencode": interpolationFuncJSONEncode(), "jsonencode": interpolationFuncJSONEncode(),
"length": interpolationFuncLength(), "length": interpolationFuncLength(),
"list": interpolationFuncList(),
"lower": interpolationFuncLower(), "lower": interpolationFuncLower(),
"md5": interpolationFuncMd5(), "md5": interpolationFuncMd5(),
"uuid": interpolationFuncUUID(), "uuid": interpolationFuncUUID(),
@ -83,6 +84,26 @@ func Funcs() map[string]ast.Function {
} }
} }
// interpolationFuncList creates a list from the parameters passed
// to it.
func interpolationFuncList() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{},
ReturnType: ast.TypeList,
Variadic: true,
VariadicType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
var outputList []string
for _, val := range args {
outputList = append(outputList, val.(string))
}
return stringSliceToVariableValue(outputList), nil
},
}
}
// interpolationFuncCompact strips a list of multi-variable values // interpolationFuncCompact strips a list of multi-variable values
// (e.g. as returned by "split") of any empty strings. // (e.g. as returned by "split") of any empty strings.
func interpolationFuncCompact() ast.Function { func interpolationFuncCompact() ast.Function {

View File

@ -12,6 +12,55 @@ import (
"github.com/hashicorp/hil/ast" "github.com/hashicorp/hil/ast"
) )
func TestInterpolateFuncList(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// empty input returns empty list
{
`${list()}`,
[]interface{}{},
false,
},
// single input returns list of length 1
{
`${list("hello")}`,
[]interface{}{"hello"},
false,
},
// two inputs returns list of length 2
{
`${list("hello", "world")}`,
[]interface{}{"hello", "world"},
false,
},
// not a string input gives error
{
`${list("hello", "${var.list}")}`,
nil,
true,
},
},
Vars: map[string]ast.Variable{
"var.list": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "Hello",
},
{
Type: ast.TypeString,
Value: "World",
},
},
},
},
})
}
func TestInterpolateFuncCompact(t *testing.T) { func TestInterpolateFuncCompact(t *testing.T) {
testFunction(t, testFunctionConfig{ testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{ Cases: []testFunctionCase{

View File

@ -168,6 +168,11 @@ The supported built-in functions are:
* `${length(split(",", "a,b,c"))}` = 3 * `${length(split(",", "a,b,c"))}` = 3
* `${length("a,b,c")}` = 5 * `${length("a,b,c")}` = 5
* `list(items...)` - Returns a list consisting of the arguments to the function.
This function provides a way of representing list literals in interpolation.
* `${list("a", "b", "c")}` returns a list of `"a", "b", "c"`.
* `${list()}` returns an empty list.
* `lookup(map, key [, default])` - Performs a dynamic lookup into a mapping * `lookup(map, key [, default])` - Performs a dynamic lookup into a mapping
variable. The `map` parameter should be another variable, such variable. The `map` parameter should be another variable, such
as `var.amis`. If `key` does not exist in `map`, the interpolation will as `var.amis`. If `key` does not exist in `map`, the interpolation will