config: introduce StringList to abstract over list hack

This is the initial pure "all tests passing without a diff" stage. The
plan is to change the internal representation of StringList to include a
suffix delimiter, which will allow us to recognize empty and
single-element lists.
This commit is contained in:
Paul Hinze 2015-06-10 11:29:44 -05:00
parent 6b84405aa9
commit 10b3abf405
7 changed files with 106 additions and 46 deletions

View File

@ -131,8 +131,8 @@ func interpolationFuncFormatList() ast.Function {
if !ok {
continue
}
parts := strings.Split(s, InterpSplitDelim)
if len(parts) == 1 {
parts := StringList(s).Slice()
if len(parts) <= 1 {
continue
}
varargs[i-1] = parts
@ -167,7 +167,7 @@ func interpolationFuncFormatList() ast.Function {
}
list[i] = fmt.Sprintf(format, fmtargs...)
}
return strings.Join(list, InterpSplitDelim), nil
return NewStringList(list).String(), nil
},
}
}
@ -181,7 +181,7 @@ func interpolationFuncJoin() ast.Function {
Callback: func(args []interface{}) (interface{}, error) {
var list []string
for _, arg := range args[1:] {
parts := strings.Split(arg.(string), InterpSplitDelim)
parts := StringList(arg.(string)).Slice()
list = append(list, parts...)
}
@ -223,18 +223,15 @@ func interpolationFuncLength() ast.Function {
ReturnType: ast.TypeInt,
Variadic: false,
Callback: func(args []interface{}) (interface{}, error) {
if !strings.Contains(args[0].(string), InterpSplitDelim) {
if !IsStringList(args[0].(string)) {
return len(args[0].(string)), nil
}
var list []string
length := 0
for _, arg := range args {
parts := strings.Split(arg.(string), InterpSplitDelim)
for _, part := range parts {
list = append(list, part)
}
length += StringList(arg.(string)).Length()
}
return len(list), nil
return length, nil
},
}
}
@ -246,7 +243,9 @@ func interpolationFuncSplit() ast.Function {
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
return strings.Replace(args[1].(string), args[0].(string), InterpSplitDelim, -1), nil
sep := args[0].(string)
s := args[1].(string)
return NewStringList(strings.Split(s, sep)).String(), nil
},
}
}
@ -284,7 +283,7 @@ func interpolationFuncElement() ast.Function {
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
list := strings.Split(args[0].(string), InterpSplitDelim)
list := StringList(args[0].(string))
index, err := strconv.Atoi(args[1].(string))
if err != nil {
@ -292,7 +291,7 @@ func interpolationFuncElement() ast.Function {
"invalid number for index, got %s", args[1])
}
v := list[index%len(list)]
v := list.Element(index)
return v, nil
},
}
@ -323,7 +322,7 @@ func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
sort.Strings(keys)
return strings.Join(keys, InterpSplitDelim), nil
return NewStringList(keys).String(), nil
},
}
}
@ -363,7 +362,7 @@ func interpolationFuncValues(vs map[string]ast.Variable) ast.Function {
vals = append(vals, vs[k].Value.(string))
}
return strings.Join(vals, InterpSplitDelim), nil
return NewStringList(vals).String(), nil
},
}
}

View File

@ -204,7 +204,7 @@ func TestInterpolateFuncFormatList(t *testing.T) {
// formatlist applies to each list element in turn
{
`${formatlist("<%s>", split(",", "A,B"))}`,
"<A>" + InterpSplitDelim + "<B>",
"<A>" + StringListDelim + "<B>",
false,
},
// formatlist repeats scalar elements
@ -268,8 +268,8 @@ func TestInterpolateFuncJoin(t *testing.T) {
fmt.Sprintf(`${join(".", "%s")}`,
fmt.Sprintf(
"foo%sbar%sbaz",
InterpSplitDelim,
InterpSplitDelim)),
StringListDelim,
StringListDelim)),
"foo.bar.baz",
false,
},
@ -396,9 +396,9 @@ func TestInterpolateFuncSplit(t *testing.T) {
`${split(",", ",,,")}`,
fmt.Sprintf(
"%s%s%s",
InterpSplitDelim,
InterpSplitDelim,
InterpSplitDelim),
StringListDelim,
StringListDelim,
StringListDelim),
false,
},
@ -407,7 +407,7 @@ func TestInterpolateFuncSplit(t *testing.T) {
fmt.Sprintf(
"%s%s",
"foo",
InterpSplitDelim),
StringListDelim),
false,
},
@ -415,9 +415,9 @@ func TestInterpolateFuncSplit(t *testing.T) {
`${split(",", ",foo,")}`,
fmt.Sprintf(
"%s%s%s",
InterpSplitDelim,
StringListDelim,
"foo",
InterpSplitDelim),
StringListDelim),
false,
},
@ -425,8 +425,8 @@ func TestInterpolateFuncSplit(t *testing.T) {
`${split(".", "foo.bar.baz")}`,
fmt.Sprintf(
"foo%sbar%sbaz",
InterpSplitDelim,
InterpSplitDelim),
StringListDelim,
StringListDelim),
false,
},
},
@ -486,7 +486,7 @@ func TestInterpolateFuncKeys(t *testing.T) {
`${keys("foo")}`,
fmt.Sprintf(
"bar%squx",
InterpSplitDelim),
StringListDelim),
false,
},
@ -535,7 +535,7 @@ func TestInterpolateFuncValues(t *testing.T) {
`${values("foo")}`,
fmt.Sprintf(
"quack%sbaz",
InterpSplitDelim),
StringListDelim),
false,
},
@ -568,7 +568,7 @@ func TestInterpolateFuncElement(t *testing.T) {
Cases: []testFunctionCase{
{
fmt.Sprintf(`${element("%s", "1")}`,
"foo"+InterpSplitDelim+"baz"),
"foo"+StringListDelim+"baz"),
"baz",
false,
},
@ -582,7 +582,7 @@ func TestInterpolateFuncElement(t *testing.T) {
// Invalid index should wrap vs. out-of-bounds
{
fmt.Sprintf(`${element("%s", "2")}`,
"foo"+InterpSplitDelim+"baz"),
"foo"+StringListDelim+"baz"),
"foo",
false,
},
@ -590,7 +590,7 @@ func TestInterpolateFuncElement(t *testing.T) {
// Too many args
{
fmt.Sprintf(`${element("%s", "0", "2")}`,
"foo"+InterpSplitDelim+"baz"),
"foo"+StringListDelim+"baz"),
nil,
true,
},

View File

@ -10,10 +10,6 @@ import (
"github.com/mitchellh/reflectwalk"
)
// InterpSplitDelim is the delimeter that is looked for to split when
// it is returned.
const InterpSplitDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6`
// interpolationWalker implements interfaces for the reflectwalk package
// (github.com/mitchellh/reflectwalk) that can be used to automatically
// execute a callback for an interpolation.
@ -149,7 +145,7 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
// splitting (in a SliceElem) or not.
remove := false
if w.loc == reflectwalk.SliceElem {
parts := strings.Split(replaceVal, InterpSplitDelim)
parts := StringList(replaceVal).Slice()
for _, p := range parts {
if p == UnknownVariableValue {
remove = true
@ -247,7 +243,7 @@ func (w *interpolationWalker) splitSlice() {
if !ok {
continue
}
if idx := strings.Index(sv, InterpSplitDelim); idx >= 0 {
if IsStringList(sv) {
split = true
break
}
@ -270,7 +266,7 @@ func (w *interpolationWalker) splitSlice() {
}
// Split on the delimiter
for _, p := range strings.Split(sv, InterpSplitDelim) {
for _, p := range StringList(sv).Slice() {
result = append(result, p)
}
}

View File

@ -157,7 +157,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
"bing",
},
},
Value: "bar" + InterpSplitDelim + "baz",
Value: "bar" + StringListDelim + "baz",
},
{
@ -168,7 +168,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
},
},
Output: map[string]interface{}{},
Value: UnknownVariableValue + InterpSplitDelim + "baz",
Value: UnknownVariableValue + StringListDelim + "baz",
},
}

65
config/string_list.go Normal file
View File

@ -0,0 +1,65 @@
package config
import "strings"
// StringList represents the "poor man's list" that terraform uses
// internally
type StringList string
// This is the delimiter used to recognize and split StringLists
const StringListDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6`
// Build a StringList from a slice
func NewStringList(parts []string) StringList {
// FOR NOW:
return StringList(strings.Join(parts, StringListDelim))
// EVENTUALLY:
// var sl StringList
// for _, p := range parts {
// sl = sl.Append(p)
// }
// return sl
}
// Returns a new StringList with the item appended
func (sl StringList) Append(s string) StringList {
// FOR NOW:
return StringList(strings.Join(append(sl.Slice(), s), StringListDelim))
// EVENTUALLY:
// return StringList(fmt.Sprintf("%s%s%s", sl, s, StringListDelim))
}
// Returns an element at the index, wrapping around the length of the string
// when index > list length
func (sl StringList) Element(index int) string {
return sl.Slice()[index%sl.Length()]
}
// Returns the length of the StringList
func (sl StringList) Length() int {
return len(sl.Slice())
}
// Returns a slice of strings as represented by this StringList
func (sl StringList) Slice() []string {
parts := strings.Split(string(sl), StringListDelim)
// FOR NOW:
if sl.String() == "" {
return []string{}
} else {
return parts
}
// EVENTUALLY:
// StringLists always have a trailing StringListDelim
// return parts[:len(parts)-1]
}
func (sl StringList) String() string {
return string(sl)
}
// Determines if a given string represents a StringList
func IsStringList(s string) bool {
return strings.Contains(s, StringListDelim)
}

View File

@ -582,7 +582,7 @@ func TestSchemaMap_Diff(t *testing.T) {
},
ConfigVariables: map[string]string{
"var.foo": "2" + config.InterpSplitDelim + "5",
"var.foo": "2" + config.StringListDelim + "5",
},
Diff: &terraform.InstanceDiff{
@ -627,7 +627,7 @@ func TestSchemaMap_Diff(t *testing.T) {
ConfigVariables: map[string]string{
"var.foo": config.UnknownVariableValue +
config.InterpSplitDelim + "5",
config.StringListDelim + "5",
},
Diff: &terraform.InstanceDiff{
@ -905,7 +905,7 @@ func TestSchemaMap_Diff(t *testing.T) {
},
ConfigVariables: map[string]string{
"var.foo": "2" + config.InterpSplitDelim + "5",
"var.foo": "2" + config.StringListDelim + "5",
},
Diff: &terraform.InstanceDiff{
@ -953,7 +953,7 @@ func TestSchemaMap_Diff(t *testing.T) {
ConfigVariables: map[string]string{
"var.foo": config.UnknownVariableValue +
config.InterpSplitDelim + "5",
config.StringListDelim + "5",
},
Diff: &terraform.InstanceDiff{

View File

@ -453,7 +453,7 @@ func (i *Interpolater) computeResourceMultiVariable(
v.FullKey())
}
return strings.Join(values, config.InterpSplitDelim), nil
return config.NewStringList(values).String(), nil
}
func (i *Interpolater) resourceVariableInfo(