Add a substring interpolation function (#12870)

Adds a new `substr` interpolation function which can be used to truncate a string.
This commit is contained in:
Joshua Spence 2017-03-23 02:30:39 +11:00 committed by Jake Champlin
parent 87d6935780
commit e71d6d92ad
3 changed files with 106 additions and 0 deletions

View File

@ -88,6 +88,7 @@ func Funcs() map[string]ast.Function {
"slice": interpolationFuncSlice(), "slice": interpolationFuncSlice(),
"sort": interpolationFuncSort(), "sort": interpolationFuncSort(),
"split": interpolationFuncSplit(), "split": interpolationFuncSplit(),
"substr": interpolationFuncSubstr(),
"timestamp": interpolationFuncTimestamp(), "timestamp": interpolationFuncTimestamp(),
"title": interpolationFuncTitle(), "title": interpolationFuncTitle(),
"trimspace": interpolationFuncTrimSpace(), "trimspace": interpolationFuncTrimSpace(),
@ -1183,3 +1184,48 @@ func interpolationFuncTitle() ast.Function {
}, },
} }
} }
// interpolationFuncSubstr implements the "substr" function that allows strings
// to be truncated.
func interpolationFuncSubstr() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{
ast.TypeString, // input string
ast.TypeInt, // offset
ast.TypeInt, // length
},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
str := args[0].(string)
offset := args[1].(int)
length := args[2].(int)
// Interpret a negative offset as being equivalent to a positive
// offset taken from the end of the string.
if offset < 0 {
offset += len(str)
}
// Interpret a length of `-1` as indicating that the substring
// should start at `offset` and continue until the end of the
// string. Any other negative length (other than `-1`) is invalid.
if length == -1 {
length = len(str)
} else if length >= 0 {
length += offset
} else {
return nil, fmt.Errorf("length should be a non-negative integer")
}
if offset > len(str) {
return nil, fmt.Errorf("offset cannot be larger than the length of the string")
}
if length > len(str) {
return nil, fmt.Errorf("'offset + length' cannot be larger than the length of the string")
}
return str[offset:length], nil
},
}
}

View File

@ -2071,3 +2071,61 @@ func TestInterpolateFuncPathExpand(t *testing.T) {
}, },
}) })
} }
func TestInterpolateFuncSubstr(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
{
`${substr("foobar", 0, 0)}`,
"",
false,
},
{
`${substr("foobar", 0, -1)}`,
"foobar",
false,
},
{
`${substr("foobar", 0, 3)}`,
"foo",
false,
},
{
`${substr("foobar", 3, 3)}`,
"bar",
false,
},
{
`${substr("foobar", -3, 3)}`,
"bar",
false,
},
// empty string
{
`${substr("", 0, 0)}`,
"",
false,
},
// invalid offset
{
`${substr("", 1, 0)}`,
nil,
true,
},
// invalid length
{
`${substr("", 0, 1)}`,
nil,
true,
},
{
`${substr("", 0, -2)}`,
nil,
true,
},
},
})
}

View File

@ -318,6 +318,8 @@ The supported built-in functions are:
`a_resource_param = ["${split(",", var.CSV_STRING)}"]`. `a_resource_param = ["${split(",", var.CSV_STRING)}"]`.
Example: `split(",", module.amod.server_ids)` Example: `split(",", module.amod.server_ids)`
* `substr(string, offset, length)` - Extracts a substring from the input string. A negative offset is interpreted as being equivalent to a positive offset measured backwards from the end of the string. A length of `-1` is interpretted as meaning "until the end of the string".
* `timestamp()` - Returns a UTC timestamp string in RFC 3339 format. This string will change with every * `timestamp()` - Returns a UTC timestamp string in RFC 3339 format. This string will change with every
invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the
[`ignore_changes`](/docs/configuration/resources.html#ignore-changes) lifecycle attribute. [`ignore_changes`](/docs/configuration/resources.html#ignore-changes) lifecycle attribute.