core: Add zipmap interpolation function

This commit adds a new interpolation function, zipmap, which produces a
map given a list of string keys and a list of values of the same length
as the list of keys.

The name comes from the same operation in Clojure (and likely other
functional langauges).
This commit is contained in:
James Nugent 2016-10-26 09:33:54 -05:00
parent fbcd1cff3f
commit 47bce79b29
3 changed files with 150 additions and 0 deletions

View File

@ -84,6 +84,7 @@ func Funcs() map[string]ast.Function {
"title": interpolationFuncTitle(),
"trimspace": interpolationFuncTrimSpace(),
"upper": interpolationFuncUpper(),
"zipmap": interpolationFuncZipMap(),
}
}
@ -386,6 +387,39 @@ func interpolationFuncFormat() ast.Function {
}
}
func interpolationFuncZipMap() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{
ast.TypeList, // Keys
ast.TypeList, // Values
},
ReturnType: ast.TypeMap,
Callback: func(args []interface{}) (interface{}, error) {
keys := args[0].([]ast.Variable)
values := args[1].([]ast.Variable)
if len(keys) != len(values) {
return nil, fmt.Errorf("count of keys (%d) does not match count of values (%d)",
len(keys), len(values))
}
for i, val := range keys {
if val.Type != ast.TypeString {
return nil, fmt.Errorf("keys must be strings. value at position %d is %s",
i, val.Type.Printable())
}
}
result := map[string]ast.Variable{}
for i := 0; i < len(keys); i++ {
result[keys[i].Value.(string)] = values[i]
}
return result, nil
},
}
}
// interpolationFuncFormatList implements the "formatlist" function that does
// string formatting on lists.
func interpolationFuncFormatList() ast.Function {

View File

@ -11,6 +11,115 @@ import (
"github.com/hashicorp/hil/ast"
)
func TestInterpolateFuncZipMap(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${zipmap(var.list, var.list2)}`,
map[string]interface{}{
"Hello": "bar",
"World": "baz",
},
false,
},
{
`${zipmap(var.list, var.nonstrings)}`,
map[string]interface{}{
"Hello": []interface{}{"bar", "baz"},
"World": []interface{}{"boo", "foo"},
},
false,
},
{
`${zipmap(var.nonstrings, var.list2)}`,
nil,
true,
},
{
`${zipmap(var.list, var.differentlengthlist)}`,
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",
},
},
},
"var.list2": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "bar",
},
{
Type: ast.TypeString,
Value: "baz",
},
},
},
"var.differentlengthlist": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "bar",
},
{
Type: ast.TypeString,
Value: "baz",
},
{
Type: ast.TypeString,
Value: "boo",
},
},
},
"var.nonstrings": {
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "bar",
},
{
Type: ast.TypeString,
Value: "baz",
},
},
},
{
Type: ast.TypeList,
Value: []ast.Variable{
{
Type: ast.TypeString,
Value: "boo",
},
{
Type: ast.TypeString,
Value: "foo",
},
},
},
},
},
},
})
}
func TestInterpolateFuncList(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{

View File

@ -252,6 +252,13 @@ The supported built-in functions are:
returned by the `keys` function. This function only works on flat maps and
will return an error for maps that include nested lists or maps.
* `zipmap(list, list)` - Creates a map from a list of keys and a list of
values. The keys must all be of type string, and the length of the lists
must be the same.
For example, to output a mapping of AWS IAM user names to the fingerprint
of the key used to encrypt their initial password, you might use:
`zipmap(aws_iam_user.users.*.name, aws_iam_user_login_profile.users.*.key_fingerprint)`.
<a id="templates"></a>
## Templates