From d57d2c7d60fd5cfa43e83445675a87da9ce19d29 Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Wed, 14 Jun 2017 00:42:15 -0400 Subject: [PATCH] Enhancing indent function Signed-off-by: Dave Henderson --- docs/content/functions/general.md | 21 ------------- docs/content/functions/strings.md | 46 ++++++++++++++++++++++++++++ funcs.go | 1 - funcs/strings.go | 35 +++++++++++++++++++++ funcs/strings_test.go | 8 +++++ strings/strings.go | 21 +++++++++++++ strings/strings_test.go | 17 ++++++++++ test/integration/strings.bats | 30 ++++++++++++++++++ test/integration/typeconv_funcs.bats | 8 ----- typeconv.go | 15 --------- typeconv_test.go | 13 -------- 11 files changed, 157 insertions(+), 58 deletions(-) create mode 100644 strings/strings.go create mode 100644 strings/strings_test.go create mode 100644 test/integration/strings.bats diff --git a/docs/content/functions/general.md b/docs/content/functions/general.md index c448948b9..32aa68200 100644 --- a/docs/content/functions/general.md +++ b/docs/content/functions/general.md @@ -132,27 +132,6 @@ $ gomplate -f input.tmpl 1-2-3 ``` -## `indent` - -Indents a given string with the given indentation pattern. If the input string has multiple lines, each line will be indented. - -#### Example - -This function can be especially useful when adding YAML snippets into other YAML documents, where indentation is important: - -_`input.tmpl`:_ -``` -foo: -{{ `{"bar": {"baz": 2}}` | json | toYAML | indent " " }} -``` - -```console -$ gomplate -f input.tmpl -foo: - bar: - baz: 2 -``` - ## `json` Converts a JSON string into an object. Only works for JSON Objects (not Arrays or other valid JSON types). This can be used to access properties of JSON objects. diff --git a/docs/content/functions/strings.md b/docs/content/functions/strings.md index 610638dc4..5d86e32ad 100644 --- a/docs/content/functions/strings.md +++ b/docs/content/functions/strings.md @@ -76,6 +76,52 @@ $ URL=http://example.com gomplate < input.tmpl http://example.com:80 ``` +## `strings.Indent` + +**Alias:** `indent` + +Indents a string. If the input string has multiple lines, each line will be indented. + +### Usage +```go +strings.Indent [width] [indent] input +``` +```go +input | strings.Indent [width] [indent] +``` + +### Arguments + +| name | description | +|--------|-------| +| `width` | _(optional)_ number of times to repeat the `indent` string. Default: `1` | +| `indent` | _(optional)_ the string to indent with. Default: `" "` | +| `input` | the string to indent | + +### Example + +This function can be especially useful when adding YAML snippets into other YAML documents, where indentation is important: + +_`input.tmpl`:_ +``` +foo: +{{ `{"bar": {"baz": 2}}` | json | toYAML | strings.Indent " " }} +{{- `{"qux": true}` | json | toYAML | strings.Indent 2 }} + quux: +{{ `{"quuz": 42}` | json | toYAML | strings.Indent 2 " " -}} +``` + +```console +$ gomplate -f input.tmpl +foo: + bar: + baz: 2 + qux: true + + quux: + quuz: 42 +``` + ## `strings.Split` Creates a slice by splitting a string on a given delimiter. diff --git a/funcs.go b/funcs.go index 0a15b53eb..b65170a36 100644 --- a/funcs.go +++ b/funcs.go @@ -25,7 +25,6 @@ func initFuncs(data *Data) template.FuncMap { "csvByRow": typeconv.CSVByRow, "csvByColumn": typeconv.CSVByColumn, "slice": typeconv.Slice, - "indent": typeconv.indent, "join": typeconv.Join, "toJSON": typeconv.ToJSON, "toJSONPretty": typeconv.toJSONPretty, diff --git a/funcs/strings.go b/funcs/strings.go index b78fe40c0..c5117ccd3 100644 --- a/funcs/strings.go +++ b/funcs/strings.go @@ -6,9 +6,12 @@ package funcs // in templates easier. import ( + "log" "sync" "strings" + + gompstrings "github.com/hairyhenderson/gomplate/strings" ) var ( @@ -31,6 +34,7 @@ func AddStringFuncs(f map[string]interface{}) { f["toUpper"] = StrNS().ToUpper f["toLower"] = StrNS().ToLower f["trimSpace"] = StrNS().TrimSpace + f["indent"] = StrNS().Indent // these are legacy aliases with non-pipelinable arg order f["contains"] = strings.Contains @@ -98,3 +102,34 @@ func (f *StringFuncs) ToLower(s string) string { func (f *StringFuncs) TrimSpace(s string) string { return strings.TrimSpace(s) } + +// Indent - +func (f *StringFuncs) Indent(args ...interface{}) string { + input, ok := args[len(args)-1].(string) + if !ok { + log.Fatal("Indent: invalid arguments") + } + indent := " " + width := 1 + switch len(args) { + case 2: + indent, ok = args[0].(string) + if !ok { + width, ok = args[0].(int) + if !ok { + log.Fatal("Indent: invalid arguments") + } + indent = " " + } + case 3: + width, ok = args[0].(int) + if !ok { + log.Fatal("Indent: invalid arguments") + } + indent, ok = args[1].(string) + if !ok { + log.Fatal("Indent: invalid arguments") + } + } + return gompstrings.Indent(width, indent, input) +} diff --git a/funcs/strings_test.go b/funcs/strings_test.go index 69bfabd90..2d2c7b63f 100644 --- a/funcs/strings_test.go +++ b/funcs/strings_test.go @@ -14,3 +14,11 @@ func TestReplaceAll(t *testing.T) { assert.Equal(t, "ReplacedReplaced", sf.ReplaceAll("Orig", "Replaced", "OrigOrig")) } + +func TestIndent(t *testing.T) { + sf := &StringFuncs{} + assert.Equal(t, " foo\n bar\n baz", sf.Indent("foo\nbar\nbaz")) + assert.Equal(t, " foo\n bar\n baz", sf.Indent(" ", "foo\nbar\nbaz")) + assert.Equal(t, "---foo\n---bar\n---baz", sf.Indent(3, "-", "foo\nbar\nbaz")) + assert.Equal(t, " foo\n bar\n baz", sf.Indent(3, "foo\nbar\nbaz")) +} diff --git a/strings/strings.go b/strings/strings.go new file mode 100644 index 000000000..d7d2c5043 --- /dev/null +++ b/strings/strings.go @@ -0,0 +1,21 @@ +package strings + +import "strings" + +// Indent - indent each line of the string with the given indent string +func Indent(width int, indent, s string) string { + if width > 1 { + indent = strings.Repeat(indent, width) + } + var res []byte + bol := true + for i := 0; i < len(s); i++ { + c := s[i] + if bol && c != '\n' { + res = append(res, indent...) + } + res = append(res, c) + bol = c == '\n' + } + return string(res) +} diff --git a/strings/strings_test.go b/strings/strings_test.go new file mode 100644 index 000000000..03338cd86 --- /dev/null +++ b/strings/strings_test.go @@ -0,0 +1,17 @@ +package strings + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIndent(t *testing.T) { + actual := "hello\nworld\n!" + expected := " hello\n world\n !" + assert.Equal(t, expected, Indent(1, " ", actual)) + assert.Equal(t, "\n", Indent(1, " ", "\n")) + assert.Equal(t, " foo\n", Indent(1, " ", "foo\n")) + assert.Equal(t, " foo", Indent(1, " ", "foo")) + assert.Equal(t, " foo", Indent(3, " ", "foo")) +} diff --git a/test/integration/strings.bats b/test/integration/strings.bats new file mode 100644 index 000000000..9d16eb254 --- /dev/null +++ b/test/integration/strings.bats @@ -0,0 +1,30 @@ +#!/usr/bin/env bats + +load helper + +tmpdir=$(mktemp -u) + +function setup () { + mkdir -p $tmpdir +} + +function teardown () { + rm -rf $tmpdir || true +} + +@test "'strings.Indent'" { + gomplate -i '{{ strings.Indent " " "hello world" }} +{{ "hello\nmultiline\nworld" | indent 2 "-" }} +{{ "foo\nbar" | strings.Indent 2 }} + {{"hello\nworld" | strings.Indent 5 | strings.TrimSpace }} +' + [ "$status" -eq 0 ] + [[ "${output}" == " hello world +--hello +--multiline +--world + foo + bar + hello + world" ]] +} diff --git a/test/integration/typeconv_funcs.bats b/test/integration/typeconv_funcs.bats index 0b22f3111..bb6f52bd5 100644 --- a/test/integration/typeconv_funcs.bats +++ b/test/integration/typeconv_funcs.bats @@ -44,14 +44,6 @@ function teardown () { }' ]] } -@test "indent" { - gomplate -i '{{ indent " " "hello world" }}{{ "hello\nmultiline\nworld" | indent " " }}' - [ "$status" -eq 0 ] - [[ "${output}" == " hello world hello - multiline - world" ]] -} - @test "join" { gomplate -i '{{ $a := `[1, 2, 3]` | jsonArray }}{{ join $a "-" }}' [ "$status" -eq 0 ] diff --git a/typeconv.go b/typeconv.go index f2597d1a9..2ec433eb4 100644 --- a/typeconv.go +++ b/typeconv.go @@ -272,21 +272,6 @@ func (t *TypeConv) Slice(args ...interface{}) []interface{} { return args } -// Indent - indent each line of the string with the given indent string -func (t *TypeConv) indent(indent, s string) string { - var res []byte - bol := true - for i := 0; i < len(s); i++ { - c := s[i] - if bol && c != '\n' { - res = append(res, indent...) - } - res = append(res, c) - bol = c == '\n' - } - return string(res) -} - // Join concatenates the elements of a to create a single string. // The separator string sep is placed between elements in the resulting string. // diff --git a/typeconv_test.go b/typeconv_test.go index 5957d693a..d38b5352e 100644 --- a/typeconv_test.go +++ b/typeconv_test.go @@ -170,19 +170,6 @@ func TestHas(t *testing.T) { assert.True(t, ty.Has(in["baz"], "qux")) } -func TestIndent(t *testing.T) { - ty := new(TypeConv) - actual := "hello\nworld\n!" - expected := " hello\n world\n !" - assert.Equal(t, expected, ty.indent(" ", actual)) - - assert.Equal(t, "\n", ty.indent(" ", "\n")) - - assert.Equal(t, " foo\n", ty.indent(" ", "foo\n")) - - assert.Equal(t, " foo", ty.indent(" ", "foo")) -} - func TestCSV(t *testing.T) { ty := new(TypeConv) in := "first,second,third\n1,2,3\n4,5,6"