config: jsonencode function to support nested lists and maps

This commit is contained in:
Sebastian Maj 2017-05-28 00:58:40 +01:00 committed by Martin Atkins
parent 0da6b95ef8
commit 28b5708fef
3 changed files with 26 additions and 24 deletions

View File

@ -824,8 +824,7 @@ func interpolationFuncJoin() ast.Function {
} }
// interpolationFuncJSONEncode implements the "jsonencode" function that encodes // interpolationFuncJSONEncode implements the "jsonencode" function that encodes
// a string, list, or map as its JSON representation. For now, values in the // a string, list, or map as its JSON representation.
// list or map may only be strings.
func interpolationFuncJSONEncode() ast.Function { func interpolationFuncJSONEncode() ast.Function {
return ast.Function{ return ast.Function{
ArgTypes: []ast.Type{ast.TypeAny}, ArgTypes: []ast.Type{ast.TypeAny},
@ -838,28 +837,36 @@ func interpolationFuncJSONEncode() ast.Function {
toEncode = typedArg toEncode = typedArg
case []ast.Variable: case []ast.Variable:
// We preallocate the list here. Note that it's important that in
// the length 0 case, we have an empty list rather than nil, as
// they encode differently.
// XXX It would be nice to support arbitrarily nested data here. Is
// there an inverse of hil.InterfaceToVariable?
strings := make([]string, len(typedArg)) strings := make([]string, len(typedArg))
for i, v := range typedArg { for i, v := range typedArg {
if v.Type != ast.TypeString { if v.Type != ast.TypeString {
return "", fmt.Errorf("list elements must be strings") variable, _ := hil.InterfaceToVariable(typedArg)
toEncode, _ = hil.VariableToInterface(variable)
jEnc, err := json.Marshal(toEncode)
if err != nil {
return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
}
return string(jEnc), nil
} }
strings[i] = v.Value.(string) strings[i] = v.Value.(string)
} }
toEncode = strings toEncode = strings
case map[string]ast.Variable: case map[string]ast.Variable:
// XXX It would be nice to support arbitrarily nested data here. Is
// there an inverse of hil.InterfaceToVariable?
stringMap := make(map[string]string) stringMap := make(map[string]string)
for k, v := range typedArg { for k, v := range typedArg {
if v.Type != ast.TypeString { if v.Type != ast.TypeString {
return "", fmt.Errorf("map values must be strings") variable, _ := hil.InterfaceToVariable(typedArg)
toEncode, _ = hil.VariableToInterface(variable)
jEnc, err := json.Marshal(toEncode)
if err != nil {
return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode)
}
return string(jEnc), nil
} }
stringMap[k] = v.Value.(string) stringMap[k] = v.Value.(string)
} }

View File

@ -1406,8 +1406,6 @@ func TestInterpolateFuncJSONEncode(t *testing.T) {
Type: ast.TypeString, Type: ast.TypeString,
}, },
"list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}), "list": interfaceToVariableSwallowError([]string{"foo", "bar\tbaz"}),
// XXX can't use InterfaceToVariable as it converts empty slice into empty
// map.
"emptylist": ast.Variable{ "emptylist": ast.Variable{
Value: []ast.Variable{}, Value: []ast.Variable{},
Type: ast.TypeList, Type: ast.TypeList,
@ -1417,8 +1415,6 @@ func TestInterpolateFuncJSONEncode(t *testing.T) {
"ba \n z": "q\\x", "ba \n z": "q\\x",
}), }),
"emptymap": interfaceToVariableSwallowError(map[string]string{}), "emptymap": interfaceToVariableSwallowError(map[string]string{}),
// Not yet supported (but it would be nice)
"nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}), "nestedlist": interfaceToVariableSwallowError([][]string{{"foo"}}),
"nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}), "nestedmap": interfaceToVariableSwallowError(map[string][]string{"foo": {"bar"}}),
}, },
@ -1470,13 +1466,13 @@ func TestInterpolateFuncJSONEncode(t *testing.T) {
}, },
{ {
`${jsonencode(nestedlist)}`, `${jsonencode(nestedlist)}`,
nil, `[["foo"]]`,
true, false,
}, },
{ {
`${jsonencode(nestedmap)}`, `${jsonencode(nestedmap)}`,
nil, `{"foo":["bar"]}`,
true, false,
}, },
}, },
}) })

View File

@ -262,10 +262,9 @@ The supported built-in functions are:
* `join(",", aws_instance.foo.*.id)` * `join(",", aws_instance.foo.*.id)`
* `join(",", var.ami_list)` * `join(",", var.ami_list)`
* `jsonencode(item)` - Returns a JSON-encoded representation of the given * `jsonencode(value)` - Returns a JSON-encoded representation of the given
item, which may be a string, list of strings, or map from string to string. value, which can contain arbitrarily-nested lists and maps. Note that if
Note that if the item is a string, the return value includes the double the value is a string then its value will be placed in quotes.
quotes.
* `keys(map)` - Returns a lexically sorted list of the map keys. * `keys(map)` - Returns a lexically sorted list of the map keys.