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:
parent
6b84405aa9
commit
10b3abf405
|
@ -131,8 +131,8 @@ func interpolationFuncFormatList() ast.Function {
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parts := strings.Split(s, InterpSplitDelim)
|
parts := StringList(s).Slice()
|
||||||
if len(parts) == 1 {
|
if len(parts) <= 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
varargs[i-1] = parts
|
varargs[i-1] = parts
|
||||||
|
@ -167,7 +167,7 @@ func interpolationFuncFormatList() ast.Function {
|
||||||
}
|
}
|
||||||
list[i] = fmt.Sprintf(format, fmtargs...)
|
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) {
|
Callback: func(args []interface{}) (interface{}, error) {
|
||||||
var list []string
|
var list []string
|
||||||
for _, arg := range args[1:] {
|
for _, arg := range args[1:] {
|
||||||
parts := strings.Split(arg.(string), InterpSplitDelim)
|
parts := StringList(arg.(string)).Slice()
|
||||||
list = append(list, parts...)
|
list = append(list, parts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,18 +223,15 @@ func interpolationFuncLength() ast.Function {
|
||||||
ReturnType: ast.TypeInt,
|
ReturnType: ast.TypeInt,
|
||||||
Variadic: false,
|
Variadic: false,
|
||||||
Callback: func(args []interface{}) (interface{}, error) {
|
Callback: func(args []interface{}) (interface{}, error) {
|
||||||
if !strings.Contains(args[0].(string), InterpSplitDelim) {
|
if !IsStringList(args[0].(string)) {
|
||||||
return len(args[0].(string)), nil
|
return len(args[0].(string)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var list []string
|
length := 0
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
parts := strings.Split(arg.(string), InterpSplitDelim)
|
length += StringList(arg.(string)).Length()
|
||||||
for _, part := range parts {
|
|
||||||
list = append(list, part)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return len(list), nil
|
return length, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,7 +243,9 @@ func interpolationFuncSplit() ast.Function {
|
||||||
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
|
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func(args []interface{}) (interface{}, error) {
|
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},
|
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func(args []interface{}) (interface{}, error) {
|
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))
|
index, err := strconv.Atoi(args[1].(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -292,7 +291,7 @@ func interpolationFuncElement() ast.Function {
|
||||||
"invalid number for index, got %s", args[1])
|
"invalid number for index, got %s", args[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
v := list[index%len(list)]
|
v := list.Element(index)
|
||||||
return v, nil
|
return v, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -323,7 +322,7 @@ func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function {
|
||||||
|
|
||||||
sort.Strings(keys)
|
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))
|
vals = append(vals, vs[k].Value.(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(vals, InterpSplitDelim), nil
|
return NewStringList(vals).String(), nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,7 +204,7 @@ func TestInterpolateFuncFormatList(t *testing.T) {
|
||||||
// formatlist applies to each list element in turn
|
// formatlist applies to each list element in turn
|
||||||
{
|
{
|
||||||
`${formatlist("<%s>", split(",", "A,B"))}`,
|
`${formatlist("<%s>", split(",", "A,B"))}`,
|
||||||
"<A>" + InterpSplitDelim + "<B>",
|
"<A>" + StringListDelim + "<B>",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
// formatlist repeats scalar elements
|
// formatlist repeats scalar elements
|
||||||
|
@ -268,8 +268,8 @@ func TestInterpolateFuncJoin(t *testing.T) {
|
||||||
fmt.Sprintf(`${join(".", "%s")}`,
|
fmt.Sprintf(`${join(".", "%s")}`,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"foo%sbar%sbaz",
|
"foo%sbar%sbaz",
|
||||||
InterpSplitDelim,
|
StringListDelim,
|
||||||
InterpSplitDelim)),
|
StringListDelim)),
|
||||||
"foo.bar.baz",
|
"foo.bar.baz",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
@ -396,9 +396,9 @@ func TestInterpolateFuncSplit(t *testing.T) {
|
||||||
`${split(",", ",,,")}`,
|
`${split(",", ",,,")}`,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"%s%s%s",
|
"%s%s%s",
|
||||||
InterpSplitDelim,
|
StringListDelim,
|
||||||
InterpSplitDelim,
|
StringListDelim,
|
||||||
InterpSplitDelim),
|
StringListDelim),
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -407,7 +407,7 @@ func TestInterpolateFuncSplit(t *testing.T) {
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"%s%s",
|
"%s%s",
|
||||||
"foo",
|
"foo",
|
||||||
InterpSplitDelim),
|
StringListDelim),
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -415,9 +415,9 @@ func TestInterpolateFuncSplit(t *testing.T) {
|
||||||
`${split(",", ",foo,")}`,
|
`${split(",", ",foo,")}`,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"%s%s%s",
|
"%s%s%s",
|
||||||
InterpSplitDelim,
|
StringListDelim,
|
||||||
"foo",
|
"foo",
|
||||||
InterpSplitDelim),
|
StringListDelim),
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -425,8 +425,8 @@ func TestInterpolateFuncSplit(t *testing.T) {
|
||||||
`${split(".", "foo.bar.baz")}`,
|
`${split(".", "foo.bar.baz")}`,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"foo%sbar%sbaz",
|
"foo%sbar%sbaz",
|
||||||
InterpSplitDelim,
|
StringListDelim,
|
||||||
InterpSplitDelim),
|
StringListDelim),
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -486,7 +486,7 @@ func TestInterpolateFuncKeys(t *testing.T) {
|
||||||
`${keys("foo")}`,
|
`${keys("foo")}`,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"bar%squx",
|
"bar%squx",
|
||||||
InterpSplitDelim),
|
StringListDelim),
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -535,7 +535,7 @@ func TestInterpolateFuncValues(t *testing.T) {
|
||||||
`${values("foo")}`,
|
`${values("foo")}`,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"quack%sbaz",
|
"quack%sbaz",
|
||||||
InterpSplitDelim),
|
StringListDelim),
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -568,7 +568,7 @@ func TestInterpolateFuncElement(t *testing.T) {
|
||||||
Cases: []testFunctionCase{
|
Cases: []testFunctionCase{
|
||||||
{
|
{
|
||||||
fmt.Sprintf(`${element("%s", "1")}`,
|
fmt.Sprintf(`${element("%s", "1")}`,
|
||||||
"foo"+InterpSplitDelim+"baz"),
|
"foo"+StringListDelim+"baz"),
|
||||||
"baz",
|
"baz",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
@ -582,7 +582,7 @@ func TestInterpolateFuncElement(t *testing.T) {
|
||||||
// Invalid index should wrap vs. out-of-bounds
|
// Invalid index should wrap vs. out-of-bounds
|
||||||
{
|
{
|
||||||
fmt.Sprintf(`${element("%s", "2")}`,
|
fmt.Sprintf(`${element("%s", "2")}`,
|
||||||
"foo"+InterpSplitDelim+"baz"),
|
"foo"+StringListDelim+"baz"),
|
||||||
"foo",
|
"foo",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
@ -590,7 +590,7 @@ func TestInterpolateFuncElement(t *testing.T) {
|
||||||
// Too many args
|
// Too many args
|
||||||
{
|
{
|
||||||
fmt.Sprintf(`${element("%s", "0", "2")}`,
|
fmt.Sprintf(`${element("%s", "0", "2")}`,
|
||||||
"foo"+InterpSplitDelim+"baz"),
|
"foo"+StringListDelim+"baz"),
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,10 +10,6 @@ import (
|
||||||
"github.com/mitchellh/reflectwalk"
|
"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
|
// interpolationWalker implements interfaces for the reflectwalk package
|
||||||
// (github.com/mitchellh/reflectwalk) that can be used to automatically
|
// (github.com/mitchellh/reflectwalk) that can be used to automatically
|
||||||
// execute a callback for an interpolation.
|
// execute a callback for an interpolation.
|
||||||
|
@ -149,7 +145,7 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
||||||
// splitting (in a SliceElem) or not.
|
// splitting (in a SliceElem) or not.
|
||||||
remove := false
|
remove := false
|
||||||
if w.loc == reflectwalk.SliceElem {
|
if w.loc == reflectwalk.SliceElem {
|
||||||
parts := strings.Split(replaceVal, InterpSplitDelim)
|
parts := StringList(replaceVal).Slice()
|
||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
if p == UnknownVariableValue {
|
if p == UnknownVariableValue {
|
||||||
remove = true
|
remove = true
|
||||||
|
@ -247,7 +243,7 @@ func (w *interpolationWalker) splitSlice() {
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if idx := strings.Index(sv, InterpSplitDelim); idx >= 0 {
|
if IsStringList(sv) {
|
||||||
split = true
|
split = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -270,7 +266,7 @@ func (w *interpolationWalker) splitSlice() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split on the delimiter
|
// Split on the delimiter
|
||||||
for _, p := range strings.Split(sv, InterpSplitDelim) {
|
for _, p := range StringList(sv).Slice() {
|
||||||
result = append(result, p)
|
result = append(result, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
||||||
"bing",
|
"bing",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Value: "bar" + InterpSplitDelim + "baz",
|
Value: "bar" + StringListDelim + "baz",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -168,7 +168,7 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Output: map[string]interface{}{},
|
Output: map[string]interface{}{},
|
||||||
Value: UnknownVariableValue + InterpSplitDelim + "baz",
|
Value: UnknownVariableValue + StringListDelim + "baz",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -582,7 +582,7 @@ func TestSchemaMap_Diff(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
ConfigVariables: map[string]string{
|
ConfigVariables: map[string]string{
|
||||||
"var.foo": "2" + config.InterpSplitDelim + "5",
|
"var.foo": "2" + config.StringListDelim + "5",
|
||||||
},
|
},
|
||||||
|
|
||||||
Diff: &terraform.InstanceDiff{
|
Diff: &terraform.InstanceDiff{
|
||||||
|
@ -627,7 +627,7 @@ func TestSchemaMap_Diff(t *testing.T) {
|
||||||
|
|
||||||
ConfigVariables: map[string]string{
|
ConfigVariables: map[string]string{
|
||||||
"var.foo": config.UnknownVariableValue +
|
"var.foo": config.UnknownVariableValue +
|
||||||
config.InterpSplitDelim + "5",
|
config.StringListDelim + "5",
|
||||||
},
|
},
|
||||||
|
|
||||||
Diff: &terraform.InstanceDiff{
|
Diff: &terraform.InstanceDiff{
|
||||||
|
@ -905,7 +905,7 @@ func TestSchemaMap_Diff(t *testing.T) {
|
||||||
},
|
},
|
||||||
|
|
||||||
ConfigVariables: map[string]string{
|
ConfigVariables: map[string]string{
|
||||||
"var.foo": "2" + config.InterpSplitDelim + "5",
|
"var.foo": "2" + config.StringListDelim + "5",
|
||||||
},
|
},
|
||||||
|
|
||||||
Diff: &terraform.InstanceDiff{
|
Diff: &terraform.InstanceDiff{
|
||||||
|
@ -953,7 +953,7 @@ func TestSchemaMap_Diff(t *testing.T) {
|
||||||
|
|
||||||
ConfigVariables: map[string]string{
|
ConfigVariables: map[string]string{
|
||||||
"var.foo": config.UnknownVariableValue +
|
"var.foo": config.UnknownVariableValue +
|
||||||
config.InterpSplitDelim + "5",
|
config.StringListDelim + "5",
|
||||||
},
|
},
|
||||||
|
|
||||||
Diff: &terraform.InstanceDiff{
|
Diff: &terraform.InstanceDiff{
|
||||||
|
|
|
@ -453,7 +453,7 @@ func (i *Interpolater) computeResourceMultiVariable(
|
||||||
v.FullKey())
|
v.FullKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(values, config.InterpSplitDelim), nil
|
return config.NewStringList(values).String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interpolater) resourceVariableInfo(
|
func (i *Interpolater) resourceVariableInfo(
|
||||||
|
|
Loading…
Reference in New Issue