From af2c84de5a6ae7bacaa5c6b4cb1644ab1cdf08ab Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Mon, 27 Mar 2017 12:21:48 +1100 Subject: [PATCH] Add `basename` and `dirname` functions Adds `basename` and `dirname` interpolation. I want to add a `stack` tag to our infrastructure, the value of which is set to `${basename(path.cwd)}`. We currently use `${replace(path.cwd, "/^.+\\//", "")}` instead, but this is extremeley unreadable. The existance of a `basename` function would be very useful for this use case. I don't have an immediate use case for a `dirname` function, but it seemed reasonable to add it as well. --- config/interpolate_funcs.go | 25 +++++++++++++++++++ config/interpolate_funcs_test.go | 24 ++++++++++++++++++ .../docs/configuration/interpolation.html.md | 4 +++ 3 files changed, 53 insertions(+) diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index e9c1ea4e2..cc09e384c 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -11,6 +11,7 @@ import ( "io/ioutil" "math" "net" + "path/filepath" "regexp" "sort" "strconv" @@ -52,6 +53,7 @@ func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { // Funcs is the mapping of built-in functions for configuration. func Funcs() map[string]ast.Function { return map[string]ast.Function{ + "basename": interpolationFuncBasename(), "base64decode": interpolationFuncBase64Decode(), "base64encode": interpolationFuncBase64Encode(), "base64sha256": interpolationFuncBase64Sha256(), @@ -62,6 +64,7 @@ func Funcs() map[string]ast.Function { "coalesce": interpolationFuncCoalesce(), "compact": interpolationFuncCompact(), "concat": interpolationFuncConcat(), + "dirname": interpolationFuncDirname(), "distinct": interpolationFuncDistinct(), "element": interpolationFuncElement(), "file": interpolationFuncFile(), @@ -600,6 +603,17 @@ func interpolationFuncIndex() ast.Function { } } +// interpolationFuncBasename implements the "dirname" function. +func interpolationFuncDirname() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeString}, + ReturnType: ast.TypeString, + Callback: func(args []interface{}) (interface{}, error) { + return filepath.Dir(args[0].(string)), nil + }, + } +} + // interpolationFuncDistinct implements the "distinct" function that // removes duplicate elements from a list. func interpolationFuncDistinct() ast.Function { @@ -1006,6 +1020,17 @@ func interpolationFuncValues(vs map[string]ast.Variable) ast.Function { } } +// interpolationFuncBasename implements the "basename" function. +func interpolationFuncBasename() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeString}, + ReturnType: ast.TypeString, + Callback: func(args []interface{}) (interface{}, error) { + return filepath.Base(args[0].(string)), nil + }, + } +} + // interpolationFuncBase64Encode implements the "base64encode" function that // allows Base64 encoding. func interpolationFuncBase64Encode() ast.Function { diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index c5ef36da5..04e85a6d5 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -852,6 +852,18 @@ func TestInterpolateFuncMerge(t *testing.T) { } +func TestInterpolateFuncDirname(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + { + `${dirname("/foo/bar/baz")}`, + "/foo/bar", + false, + }, + }, + }) +} + func TestInterpolateFuncDistinct(t *testing.T) { testFunction(t, testFunctionConfig{ Cases: []testFunctionCase{ @@ -1777,6 +1789,18 @@ func TestInterpolateFuncElement(t *testing.T) { }) } +func TestInterpolateFuncBasename(t *testing.T) { + testFunction(t, testFunctionConfig{ + Cases: []testFunctionCase{ + { + `${basename("/foo/bar/baz")}`, + "baz", + false, + }, + }, + }) +} + func TestInterpolateFuncBase64Encode(t *testing.T) { testFunction(t, testFunctionConfig{ Cases: []testFunctionCase{ diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index f6bc3e3c8..b101730a3 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -140,6 +140,8 @@ syntax `name(arg, arg2, ...)`. For example, to read a file: The supported built-in functions are: + * `basename(path)` - Returns the last element of a path. + * `base64decode(string)` - Given a base64-encoded string, decodes it and returns the original string. @@ -183,6 +185,8 @@ The supported built-in functions are: * `concat(list1, list2, ...)` - Combines two or more lists into a single list. Example: `concat(aws_instance.db.*.tags.Name, aws_instance.web.*.tags.Name)` + * `dirname(path)` - Returns all but the last element of path, typically the path's directory. + * `distinct(list)` - Removes duplicate items from a list. Keeps the first occurrence of each element, and removes subsequent occurrences. This function is only valid for flat lists. Example: `distinct(var.usernames)`