Merge #3239: "compact" interpolation function

This commit is contained in:
Martin Atkins 2015-10-10 15:18:10 -07:00
commit 3c939f9b26
5 changed files with 84 additions and 9 deletions

View File

@ -20,6 +20,7 @@ var Funcs map[string]ast.Function
func init() { func init() {
Funcs = map[string]ast.Function{ Funcs = map[string]ast.Function{
"compact": interpolationFuncCompact(),
"concat": interpolationFuncConcat(), "concat": interpolationFuncConcat(),
"element": interpolationFuncElement(), "element": interpolationFuncElement(),
"file": interpolationFuncFile(), "file": interpolationFuncFile(),
@ -35,6 +36,22 @@ func init() {
} }
} }
// interpolationFuncCompact strips a list of multi-variable values
// (e.g. as returned by "split") of any empty strings.
func interpolationFuncCompact() ast.Function {
return ast.Function{
ArgTypes: []ast.Type{ast.TypeString},
ReturnType: ast.TypeString,
Variadic: false,
Callback: func(args []interface{}) (interface{}, error) {
if !IsStringList(args[0].(string)) {
return args[0].(string), nil
}
return StringList(args[0].(string)).Compact().String(), nil
},
}
}
// interpolationFuncConcat implements the "concat" function that // interpolationFuncConcat implements the "concat" function that
// concatenates multiple strings. This isn't actually necessary anymore // concatenates multiple strings. This isn't actually necessary anymore
// since our language supports string concat natively, but for backwards // since our language supports string concat natively, but for backwards

View File

@ -11,6 +11,33 @@ import (
"github.com/hashicorp/terraform/config/lang/ast" "github.com/hashicorp/terraform/config/lang/ast"
) )
func TestInterpolateFuncCompact(t *testing.T) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
// empty string within array
{
`${compact(split(",", "a,,b"))}`,
NewStringList([]string{"a", "b"}).String(),
false,
},
// empty string at the end of array
{
`${compact(split(",", "a,b,"))}`,
NewStringList([]string{"a", "b"}).String(),
false,
},
// single empty string
{
`${compact(split(",", ""))}`,
NewStringList([]string{}).String(),
false,
},
},
})
}
func TestInterpolateFuncDeprecatedConcat(t *testing.T) { func TestInterpolateFuncDeprecatedConcat(t *testing.T) {
testFunction(t, testFunctionConfig{ testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{ Cases: []testFunctionCase{

View File

@ -24,6 +24,20 @@ type StringList string
// ["", ""] => SLDSLDSLD // ["", ""] => SLDSLDSLD
const stringListDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6` const stringListDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6`
// Takes a Stringlist and returns one without empty strings in it
func (sl StringList) Compact() StringList {
parts := sl.Slice()
newlist := []string{}
// drop the empty strings
for i := range parts {
if parts[i] != "" {
newlist = append(newlist, parts[i])
}
}
return NewStringList(newlist)
}
// Build a StringList from a slice // Build a StringList from a slice
func NewStringList(parts []string) StringList { func NewStringList(parts []string) StringList {
// We have to special case the empty list representation // We have to special case the empty list representation
@ -55,11 +69,10 @@ func (sl StringList) Length() int {
func (sl StringList) Slice() []string { func (sl StringList) Slice() []string {
parts := strings.Split(string(sl), stringListDelim) parts := strings.Split(string(sl), stringListDelim)
switch len(parts) { // split on an empty StringList will have a length of 2, since there is
case 0, 1: // always at least one deliminator
if len(parts) <= 2 {
return []string{} return []string{}
case 2:
return []string{""}
} }
// strip empty elements generated by leading and trailing delimiters // strip empty elements generated by leading and trailing delimiters

View File

@ -27,3 +27,26 @@ func TestStringList_element(t *testing.T) {
list, expected, actual) list, expected, actual)
} }
} }
func TestStringList_empty_slice(t *testing.T) {
expected := []string{}
l := NewStringList(expected)
actual := l.Slice()
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("Expected %q, got %q", expected, actual)
}
}
func TestStringList_empty_slice_length(t *testing.T) {
list := []string{}
l := NewStringList([]string{})
actual := l.Length()
expected := 0
if actual != expected {
t.Fatalf("Expected length of %q to be %d, got %d",
list, expected, actual)
}
}

View File

@ -330,11 +330,6 @@ func TestInterpolator_resourceMultiAttributesWithResourceCount(t *testing.T) {
Value: config.NewStringList([]string{}).String(), Value: config.NewStringList([]string{}).String(),
Type: ast.TypeString, Type: ast.TypeString,
}) })
// Zero + zero elements
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.nothing", ast.Variable{
Value: config.NewStringList([]string{"", ""}).String(),
Type: ast.TypeString,
})
// Zero + 1 element // Zero + 1 element
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special", ast.Variable{ testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special", ast.Variable{
Value: config.NewStringList([]string{"extra"}).String(), Value: config.NewStringList([]string{"extra"}).String(),