From 117f44bcdaa78001a4e2835e444f340e2c6c63b8 Mon Sep 17 00:00:00 2001 From: JWal31 Date: Tue, 13 Jun 2017 12:27:45 -0700 Subject: [PATCH] config: Add "flatten" interpolation function This function turns a list of lists or any arbitrary number of nested lists into a flat list of primitive values. --- config/interpolate_funcs.go | 28 +++++++++++ config/interpolate_funcs_test.go | 46 +++++++++++++++++++ .../docs/configuration/interpolation.html.md | 4 ++ 3 files changed, 78 insertions(+) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index a298cf2d3dca..b934a5b837ca 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -76,6 +76,7 @@ func Funcs() map[string]ast.Function { "element": interpolationFuncElement(), "file": interpolationFuncFile(), "matchkeys": interpolationFuncMatchKeys(), + "flatten": interpolationFuncFlatten(), "floor": interpolationFuncFloor(), "format": interpolationFuncFormat(), "formatlist": interpolationFuncFormatList(), @@ -1453,3 +1454,30 @@ func interpolationFuncSubstr() ast.Function { }, } } + +// Flatten until it's not ast.TypeList +func flattener(finalList []ast.Variable, flattenList []ast.Variable) []ast.Variable { + for _, val := range flattenList { + if val.Type == ast.TypeList { + finalList = flattener(finalList, val.Value.([]ast.Variable)) + } else { + finalList = append(finalList, val) + } + } + return finalList +} + +// Flatten to single list +func interpolationFuncFlatten() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeList}, + ReturnType: ast.TypeList, + Variadic: false, + Callback: func(args []interface{}) (interface{}, error) { + inputList := args[0].([]ast.Variable) + + var outputList []ast.Variable + return flattener(outputList, inputList), nil + }, + } +} diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 3344c0c4bf40..9878c0fef12a 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -2503,3 +2503,49 @@ func TestInterpolateFuncBcrypt(t *testing.T) { }, }) } + +func TestInterpolateFuncFlatten(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + // empty string within array + { + `${flatten(split(",", "a,,b"))}`, + []interface{}{"a", "", "b"}, + false, + }, + + // typical array + { + `${flatten(split(",", "a,b,c"))}`, + []interface{}{"a", "b", "c"}, + false, + }, + + // empty array + { + `${flatten(split(",", ""))}`, + []interface{}{""}, + false, + }, + + // list of lists + { + `${flatten(list(list("a"), list("b")))}`, + []interface{}{"a", "b"}, + false, + }, + // list of lists of lists + { + `${flatten(list(list("a"), list(list("b","c"))))}`, + []interface{}{"a", "b", "c"}, + false, + }, + // list of strings + { + `${flatten(list("a", "b", "c"))}`, + []interface{}{"a", "b", "c"}, + false, + }, + }, + }) +} diff --git a/website/docs/configuration/interpolation.html.md b/website/docs/configuration/interpolation.html.md index 7b62162b372f..fd159889483c 100644 --- a/website/docs/configuration/interpolation.html.md +++ b/website/docs/configuration/interpolation.html.md @@ -232,6 +232,10 @@ The supported built-in functions are: * `floor(float)` - Returns the greatest integer value less than or equal to the argument. + * `flatten(list of lists)` - Flattens lists of lists down to a flat list of + primitive values, eliminating any nested lists recursively. Examples: + * `flatten(data.github_user.user.*.gpg_keys)` + * `format(format, args, ...)` - Formats a string according to the given format. The syntax for the format is standard `sprintf` syntax. Good documentation for the syntax can be [found here](https://golang.org/pkg/fmt/).