Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: compact interpolate function #3239

Merged
merged 10 commits into from
Oct 10, 2015
37 changes: 27 additions & 10 deletions config/interpolate_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,38 @@ var Funcs map[string]ast.Function

func init() {
Funcs = map[string]ast.Function{
"concat": interpolationFuncConcat(),
"element": interpolationFuncElement(),
"file": interpolationFuncFile(),
"format": interpolationFuncFormat(),
"formatlist": interpolationFuncFormatList(),
"index": interpolationFuncIndex(),
"join": interpolationFuncJoin(),
"length": interpolationFuncLength(),
"replace": interpolationFuncReplace(),
"split": interpolationFuncSplit(),
"compact": interpolationFuncCompact(),
"concat": interpolationFuncConcat(),
"element": interpolationFuncElement(),
"file": interpolationFuncFile(),
"format": interpolationFuncFormat(),
"formatlist": interpolationFuncFormatList(),
"index": interpolationFuncIndex(),
"join": interpolationFuncJoin(),
"length": interpolationFuncLength(),
"replace": interpolationFuncReplace(),
"split": interpolationFuncSplit(),
"base64encode": interpolationFuncBase64Encode(),
"base64decode": interpolationFuncBase64Decode(),
}
}

// 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
// concatenates multiple strings. This isn't actually necessary anymore
// since our language supports string concat natively, but for backwards
Expand Down
28 changes: 28 additions & 0 deletions config/interpolate_funcs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,34 @@ import (
"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) {
testFunction(t, testFunctionConfig{
Cases: []testFunctionCase{
Expand Down
21 changes: 17 additions & 4 deletions config/string_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ type StringList string
// ["", ""] => SLDSLDSLD
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
func NewStringList(parts []string) StringList {
// We have to special case the empty list representation
Expand Down Expand Up @@ -55,11 +69,10 @@ func (sl StringList) Length() int {
func (sl StringList) Slice() []string {
parts := strings.Split(string(sl), stringListDelim)

switch len(parts) {
case 0, 1:
// split on an empty StringList will have a length of 2, since there is
// always at least one deliminator
if len(parts) <= 2 {
return []string{}
case 2:
return []string{""}
}

// strip empty elements generated by leading and trailing delimiters
Expand Down
23 changes: 23 additions & 0 deletions config/string_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,26 @@ func TestStringList_element(t *testing.T) {
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)
}
}
5 changes: 0 additions & 5 deletions terraform/interpolate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,6 @@ func TestInterpolator_resourceMultiAttributesWithResourceCount(t *testing.T) {
Value: config.NewStringList([]string{}).String(),
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
testInterpolate(t, i, scope, "aws_route53_zone.terra.*.special", ast.Variable{
Value: config.NewStringList([]string{"extra"}).String(),
Expand Down