summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs-src/content/functions/coll.yml46
-rw-r--r--docs-src/content/functions/conv.yml10
-rw-r--r--docs-src/content/functions/func_doc.md.tmpl2
-rw-r--r--docs-src/content/functions/math.yml10
-rw-r--r--docs-src/content/functions/strings.yml6
-rw-r--r--docs/content/functions/coll.md57
-rw-r--r--docs/content/functions/conv.md10
-rw-r--r--docs/content/functions/data.md1
-rw-r--r--docs/content/functions/math.md10
-rw-r--r--docs/content/functions/strings.md20
-rw-r--r--docs/content/syntax.md8
-rw-r--r--funcs/coll.go17
-rw-r--r--funcs/coll_test.go34
-rw-r--r--funcs/conv.go2
-rw-r--r--gomplate_test.go11
-rw-r--r--internal/tests/integration/collection_test.go6
-rw-r--r--internal/texttemplate/exec.go24
-rw-r--r--internal/texttemplate/exec_test.go107
-rw-r--r--internal/texttemplate/funcs.go79
19 files changed, 382 insertions, 78 deletions
diff --git a/docs-src/content/functions/coll.yml b/docs-src/content/functions/coll.yml
index cf267fe1..a82e3f59 100644
--- a/docs-src/content/functions/coll.yml
+++ b/docs-src/content/functions/coll.yml
@@ -43,6 +43,7 @@ funcs:
Hello world!
Hello everybody!
- name: coll.Slice
+ deprecated: The `slice` alias is deprecated, use the full name `coll.Slice` instead.
alias: slice
description: |
Creates a slice (like an array or list). Useful when needing to `range` over a bunch of variables.
@@ -53,10 +54,39 @@ funcs:
description: the elements of the slice
examples:
- |
- $ gomplate -i '{{ range slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
+ $ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
Hello, Bart
Hello, Lisa
Hello, Maggie
+ - name: coll.GoSlice
+ description: |
+ This exposes the `slice` function from Go's [`text/template`](https://golang.org/pkg/text/template/#hdr-Functions)
+ package. Note that using `slice` will use the `coll.Slice` function instead,
+ which may not be desired.
+ For some background on this, see [this issue](https://github.com/hairyhenderson/gomplate/issues/1461).
+
+ Here is the upstream documentation:
+
+ ```
+ slice returns the result of slicing its first argument by the
+ remaining arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2],
+ while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
+ is x[1:2:3]. The first argument must be a string, slice, or array.
+ ```
+
+ See the [Go language spec](https://go.dev/ref/spec#Slice_expressions) for
+ more details.
+ pipeline: false
+ arguments:
+ - name: item
+ required: true
+ description: the string, slice, or array to slice
+ - name: indexes...
+ required: false
+ description: the indexes to slice the item by (0 to 3 arguments)
+ examples:
+ - |
+ $ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}{{ if has $l "bar" }}a{{else}}no{{end}} bar'
- name: coll.Has
alias: has
description: |
@@ -71,7 +101,7 @@ funcs:
description: The item to search for
examples:
- |
- $ gomplate -i '{{ $l := slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
+ $ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
there is a bar
- |
$ export DATA='{"foo": "bar"}'
@@ -163,7 +193,7 @@ funcs:
description: the slice or array to append to
examples:
- |
- $ gomplate -i '{{ slice 1 1 2 3 | append 5 }}'
+ $ gomplate -i '{{ coll.Slice 1 1 2 3 | append 5 }}'
[1 1 2 3 5]
- name: coll.Prepend
alias: prepend
@@ -183,7 +213,7 @@ funcs:
description: the slice or array to prepend to
examples:
- |
- $ gomplate -i '{{ slice 4 3 2 1 | prepend 5 }}'
+ $ gomplate -i '{{ coll.Slice 4 3 2 1 | prepend 5 }}'
[5 4 3 2 1]
- name: coll.Uniq
alias: uniq
@@ -198,7 +228,7 @@ funcs:
description: the input list
examples:
- |
- $ gomplate -i '{{ slice 1 2 3 2 3 4 1 5 | uniq }}'
+ $ gomplate -i '{{ coll.Slice 1 2 3 2 3 4 1 5 | uniq }}'
[1 2 3 4 5]
- name: coll.Flatten
alias: flatten
@@ -235,7 +265,7 @@ funcs:
description: the list to reverse
examples:
- |
- $ gomplate -i '{{ slice 4 3 2 1 | reverse }}'
+ $ gomplate -i '{{ coll.Slice 4 3 2 1 | reverse }}'
[1 2 3 4]
- name: coll.Sort
alias: sort
@@ -257,10 +287,10 @@ funcs:
description: the slice or array to sort
examples:
- |
- $ gomplate -i '{{ slice "foo" "bar" "baz" | coll.Sort }}'
+ $ gomplate -i '{{ coll.Slice "foo" "bar" "baz" | coll.Sort }}'
[bar baz foo]
- |
- $ gomplate -i '{{ sort (slice 3 4 1 2 5) }}'
+ $ gomplate -i '{{ sort (coll.Slice 3 4 1 2 5) }}'
[1 2 3 4 5]
- |
$ cat <<EOF > in.json
diff --git a/docs-src/content/functions/conv.yml b/docs-src/content/functions/conv.yml
index 10aa2fc3..884ba35b 100644
--- a/docs-src/content/functions/conv.yml
+++ b/docs-src/content/functions/conv.yml
@@ -65,7 +65,7 @@ funcs:
For creating more complex maps, see [`data.JSON`](../data/#data-json) or [`data.YAML`](../data/#data-yaml).
- For creating arrays, see [`conv.Slice`](#conv-slice).
+ For creating arrays, see [`coll.Slice`](#coll-slice).
arguments:
- name: in...
required: true
@@ -97,7 +97,7 @@ funcs:
description: the elements of the slice
examples:
- |
- $ gomplate -i '{{ range slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
+ $ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
Hello, Bart
Hello, Lisa
Hello, Maggie
@@ -116,7 +116,7 @@ funcs:
description: The item to search for
examples:
- |
- $ gomplate -i '{{ $l := slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
+ $ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
there is a bar
- |
$ export DATA='{"foo": "bar"}'
@@ -141,7 +141,7 @@ funcs:
description: the separator
examples:
- |
- $ gomplate -i '{{ $a := slice 1 2 3 }}{{ join $a "-" }}'
+ $ gomplate -i '{{ $a := coll.Slice 1 2 3 }}{{ join $a "-" }}'
1-2-3
- name: conv.URL
alias: urlParse
@@ -417,5 +417,5 @@ funcs:
description: the inputs to be converted
examples:
- |
- $ gomplate -i '{{ conv.ToStrings nil 42 true 0xF (slice 1 2 3) }}'
+ $ gomplate -i '{{ conv.ToStrings nil 42 true 0xF (coll.Slice 1 2 3) }}'
[nil 42 true 15 [1 2 3]]
diff --git a/docs-src/content/functions/func_doc.md.tmpl b/docs-src/content/functions/func_doc.md.tmpl
index 0e8668de..36adc712 100644
--- a/docs-src/content/functions/func_doc.md.tmpl
+++ b/docs-src/content/functions/func_doc.md.tmpl
@@ -1,7 +1,7 @@
{{ define "argName" }}{{ if not .required }}[{{ .name }}]{{else}}{{ .name }}{{end}}{{ end }}
{{- define "usage" }}### Usage
-{{- $arguments := index . "arguments" | default slice }}
+{{- $arguments := index . "arguments" | default coll.Slice }}
{{ if has . "rawUsage" }}{{ .rawUsage | strings.TrimSpace }}{{ else }}
```go
{{ .name }}{{ range $a := $arguments }} {{template "argName" $a }}{{end}}
diff --git a/docs-src/content/functions/math.yml b/docs-src/content/functions/math.yml
index 385b53d0..99c2a846 100644
--- a/docs-src/content/functions/math.yml
+++ b/docs-src/content/functions/math.yml
@@ -58,7 +58,7 @@ funcs:
description: The input number. Will be converted to a `float64`, or `0` if not convertible
examples:
- |
- $ gomplate -i '{{ range (slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}ceil {{ printf "%#v" . }} = {{ math.Ceil . }}{{"\n"}}{{ end }}'
+ $ gomplate -i '{{ range (coll.Slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}ceil {{ printf "%#v" . }} = {{ math.Ceil . }}{{"\n"}}{{ end }}'
ceil 5.1 = 6
ceil 42 = 42
ceil "3.14" = 4
@@ -93,7 +93,7 @@ funcs:
description: The input number. Will be converted to a `float64`, or `0` if not convertable
examples:
- |
- $ gomplate -i '{{ range (slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}floor {{ printf "%#v" . }} = {{ math.Floor . }}{{"\n"}}{{ end }}'
+ $ gomplate -i '{{ range (coll.Slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}floor {{ printf "%#v" . }} = {{ math.Floor . }}{{"\n"}}{{ end }}'
floor 5.1 = 4
floor 42 = 42
floor "3.14" = 3
@@ -112,7 +112,7 @@ funcs:
description: The value to test
examples:
- |
- $ gomplate -i '{{ range (slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsFloat .) }}{{.}} is a float{{"\n"}}{{ end }}{{end}}'
+ $ gomplate -i '{{ range (coll.Slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsFloat .) }}{{.}} is a float{{"\n"}}{{ end }}{{end}}'
1 is a float
-1.0 is a float
5.1 is a float
@@ -128,7 +128,7 @@ funcs:
description: The value to test
examples:
- |
- $ gomplate -i '{{ range (slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsInt .) }}{{.}} is an integer{{"\n"}}{{ end }}{{end}}'
+ $ gomplate -i '{{ range (coll.Slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsInt .) }}{{.}} is an integer{{"\n"}}{{ end }}{{end}}'
42 is an integer
0xFF is an integer
-0 is an integer
@@ -225,7 +225,7 @@ funcs:
description: The input number. Will be converted to a `float64`, or `0` if not convertable
examples:
- |
- $ gomplate -i '{{ range (slice -6.5 5.1 42.9 "3.5" 6.5) }}round {{ printf "%#v" . }} = {{ math.Round . }}{{"\n"}}{{ end }}'
+ $ gomplate -i '{{ range (coll.Slice -6.5 5.1 42.9 "3.5" 6.5) }}round {{ printf "%#v" . }} = {{ math.Round . }}{{"\n"}}{{ end }}'
round -6.5 = -7
round 5.1 = 5
round 42.9 = 43
diff --git a/docs-src/content/functions/strings.yml b/docs-src/content/functions/strings.yml
index bdc127aa..b72f7f7b 100644
--- a/docs-src/content/functions/strings.yml
+++ b/docs-src/content/functions/strings.yml
@@ -135,7 +135,7 @@ funcs:
description: The list to sort
examples:
- |
- $ gomplate -i '{{ (slice "foo" "bar" "baz") | strings.Sort }}'
+ $ gomplate -i '{{ (coll.Slice "foo" "bar" "baz") | strings.Sort }}'
[bar baz foo]
- name: strings.Split
description: |
@@ -267,7 +267,7 @@ funcs:
description: The input to quote
examples:
- |
- $ gomplate -i "{{ slice \"one word\" \"foo='bar baz'\" | shellQuote }}"
+ $ gomplate -i "{{ coll.Slice \"one word\" \"foo='bar baz'\" | shellQuote }}"
'one word' 'foo='"'"'bar baz'"'"''
- |
$ gomplate -i "{{ strings.ShellQuote \"it's a banana\" }}"
@@ -518,7 +518,7 @@ funcs:
description: the input(s) to measure
examples:
- |
- $ gomplate -i '{{ range (slice "\u03a9" "\u0030" "\u1430") }}{{ printf "%s is %d bytes and %d runes\n" . (len .) (strings.RuneCount .) }}{{ end }}'
+ $ gomplate -i '{{ range (coll.Slice "\u03a9" "\u0030" "\u1430") }}{{ printf "%s is %d bytes and %d runes\n" . (len .) (strings.RuneCount .) }}{{ end }}'
Ω is 2 bytes and 1 runes
0 is 1 bytes and 1 runes
ᐰ is 3 bytes and 1 runes
diff --git a/docs/content/functions/coll.md b/docs/content/functions/coll.md
index 31878333..75446aa0 100644
--- a/docs/content/functions/coll.md
+++ b/docs/content/functions/coll.md
@@ -60,7 +60,8 @@ Hello world!
Hello everybody!
```
-## `coll.Slice`
+## `coll.Slice` _(deprecated)_
+**Deprecation Notice:** The `slice` alias is deprecated, use the full name `coll.Slice` instead.
**Alias:** `slice`
@@ -81,12 +82,50 @@ coll.Slice in...
### Examples
```console
-$ gomplate -i '{{ range slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
+$ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
Hello, Bart
Hello, Lisa
Hello, Maggie
```
+## `coll.GoSlice`
+
+This exposes the `slice` function from Go's [`text/template`](https://golang.org/pkg/text/template/#hdr-Functions)
+package. Note that using `slice` will use the `coll.Slice` function instead,
+which may not be desired.
+For some background on this, see [this issue](https://github.com/hairyhenderson/gomplate/issues/1461).
+
+Here is the upstream documentation:
+
+```
+slice returns the result of slicing its first argument by the
+remaining arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2],
+while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
+is x[1:2:3]. The first argument must be a string, slice, or array.
+```
+
+See the [Go language spec](https://go.dev/ref/spec#Slice_expressions) for
+more details.
+
+### Usage
+
+```go
+coll.GoSlice item [indexes...]
+```
+
+### Arguments
+
+| name | description |
+|------|-------------|
+| `item` | _(required)_ the string, slice, or array to slice |
+| `indexes...` | _(optional)_ the indexes to slice the item by (0 to 3 arguments) |
+
+### Examples
+
+```console
+$ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}{{ if has $l "bar" }}a{{else}}no{{end}} bar'
+```
+
## `coll.Has`
**Alias:** `has`
@@ -109,7 +148,7 @@ coll.Has in item
### Examples
```console
-$ gomplate -i '{{ $l := slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
+$ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
there is a bar
```
```console
@@ -259,7 +298,7 @@ list... | coll.Append value
### Examples
```console
-$ gomplate -i '{{ slice 1 1 2 3 | append 5 }}'
+$ gomplate -i '{{ coll.Slice 1 1 2 3 | append 5 }}'
[1 1 2 3 5]
```
@@ -292,7 +331,7 @@ list... | coll.Prepend value
### Examples
```console
-$ gomplate -i '{{ slice 4 3 2 1 | prepend 5 }}'
+$ gomplate -i '{{ coll.Slice 4 3 2 1 | prepend 5 }}'
[5 4 3 2 1]
```
@@ -322,7 +361,7 @@ list | coll.Uniq
### Examples
```console
-$ gomplate -i '{{ slice 1 2 3 2 3 4 1 5 | uniq }}'
+$ gomplate -i '{{ coll.Slice 1 2 3 2 3 4 1 5 | uniq }}'
[1 2 3 4 5]
```
@@ -388,7 +427,7 @@ list | coll.Reverse
### Examples
```console
-$ gomplate -i '{{ slice 4 3 2 1 | reverse }}'
+$ gomplate -i '{{ coll.Slice 4 3 2 1 | reverse }}'
[1 2 3 4]
```
@@ -423,11 +462,11 @@ list | coll.Sort [key]
### Examples
```console
-$ gomplate -i '{{ slice "foo" "bar" "baz" | coll.Sort }}'
+$ gomplate -i '{{ coll.Slice "foo" "bar" "baz" | coll.Sort }}'
[bar baz foo]
```
```console
-$ gomplate -i '{{ sort (slice 3 4 1 2 5) }}'
+$ gomplate -i '{{ sort (coll.Slice 3 4 1 2 5) }}'
[1 2 3 4 5]
```
```console
diff --git a/docs/content/functions/conv.md b/docs/content/functions/conv.md
index 32045e1d..d150bf15 100644
--- a/docs/content/functions/conv.md
+++ b/docs/content/functions/conv.md
@@ -96,7 +96,7 @@ function, as used in [Helm templates](https://docs.helm.sh/chart_template_guide#
For creating more complex maps, see [`data.JSON`](../data/#data-json) or [`data.YAML`](../data/#data-yaml).
-For creating arrays, see [`conv.Slice`](#conv-slice).
+For creating arrays, see [`coll.Slice`](#coll-slice).
### Usage
@@ -151,7 +151,7 @@ conv.Slice in...
### Examples
```console
-$ gomplate -i '{{ range slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
+$ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
Hello, Bart
Hello, Lisa
Hello, Maggie
@@ -180,7 +180,7 @@ conv.Has in item
### Examples
```console
-$ gomplate -i '{{ $l := slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
+$ gomplate -i '{{ $l := coll.Slice "foo" "bar" "baz" }}there is {{ if has $l "bar" }}a{{else}}no{{end}} bar'
there is a bar
```
```console
@@ -218,7 +218,7 @@ conv.Join in sep
### Examples
```console
-$ gomplate -i '{{ $a := slice 1 2 3 }}{{ join $a "-" }}'
+$ gomplate -i '{{ $a := coll.Slice 1 2 3 }}{{ join $a "-" }}'
1-2-3
```
@@ -668,6 +668,6 @@ conv.ToStrings in...
### Examples
```console
-$ gomplate -i '{{ conv.ToStrings nil 42 true 0xF (slice 1 2 3) }}'
+$ gomplate -i '{{ conv.ToStrings nil 42 true 0xF (coll.Slice 1 2 3) }}'
[nil 42 true 15 [1 2 3]]
```
diff --git a/docs/content/functions/data.md b/docs/content/functions/data.md
index 31d2b27c..c1bac8ca 100644
--- a/docs/content/functions/data.md
+++ b/docs/content/functions/data.md
@@ -107,6 +107,7 @@ Lists all the datasources defined, list returned will be sorted in ascending ord
listDatasources
```
+
### Examples
```console
diff --git a/docs/content/functions/math.md b/docs/content/functions/math.md
index 82a656e1..7250db1a 100644
--- a/docs/content/functions/math.md
+++ b/docs/content/functions/math.md
@@ -98,7 +98,7 @@ math.Ceil num
### Examples
```console
-$ gomplate -i '{{ range (slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}ceil {{ printf "%#v" . }} = {{ math.Ceil . }}{{"\n"}}{{ end }}'
+$ gomplate -i '{{ range (coll.Slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}ceil {{ printf "%#v" . }} = {{ math.Ceil . }}{{"\n"}}{{ end }}'
ceil 5.1 = 6
ceil 42 = 42
ceil "3.14" = 4
@@ -158,7 +158,7 @@ math.Floor num
### Examples
```console
-$ gomplate -i '{{ range (slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}floor {{ printf "%#v" . }} = {{ math.Floor . }}{{"\n"}}{{ end }}'
+$ gomplate -i '{{ range (coll.Slice 5.1 42 "3.14" "0xFF" "NaN" "Inf" "-0") }}floor {{ printf "%#v" . }} = {{ math.Floor . }}{{"\n"}}{{ end }}'
floor 5.1 = 4
floor 42 = 42
floor "3.14" = 3
@@ -189,7 +189,7 @@ math.IsFloat num
### Examples
```console
-$ gomplate -i '{{ range (slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsFloat .) }}{{.}} is a float{{"\n"}}{{ end }}{{end}}'
+$ gomplate -i '{{ range (coll.Slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsFloat .) }}{{.}} is a float{{"\n"}}{{ end }}{{end}}'
1 is a float
-1.0 is a float
5.1 is a float
@@ -217,7 +217,7 @@ math.IsInt num
### Examples
```console
-$ gomplate -i '{{ range (slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsInt .) }}{{.}} is an integer{{"\n"}}{{ end }}{{end}}'
+$ gomplate -i '{{ range (coll.Slice 1.0 "-1.0" 5.1 42 "3.14" "foo" "0xFF" "NaN" "Inf" "-0") }}{{ if (math.IsInt .) }}{{.}} is an integer{{"\n"}}{{ end }}{{end}}'
42 is an integer
0xFF is an integer
-0 is an integer
@@ -399,7 +399,7 @@ math.Round num
### Examples
```console
-$ gomplate -i '{{ range (slice -6.5 5.1 42.9 "3.5" 6.5) }}round {{ printf "%#v" . }} = {{ math.Round . }}{{"\n"}}{{ end }}'
+$ gomplate -i '{{ range (coll.Slice -6.5 5.1 42.9 "3.5" 6.5) }}round {{ printf "%#v" . }} = {{ math.Round . }}{{"\n"}}{{ end }}'
round -6.5 = -7
round 5.1 = 5
round 42.9 = 43
diff --git a/docs/content/functions/strings.md b/docs/content/functions/strings.md
index 65b4193c..dc00267e 100644
--- a/docs/content/functions/strings.md
+++ b/docs/content/functions/strings.md
@@ -203,7 +203,7 @@ list | strings.Sort
### Examples
```console
-$ gomplate -i '{{ (slice "foo" "bar" "baz") | strings.Sort }}'
+$ gomplate -i '{{ (coll.Slice "foo" "bar" "baz") | strings.Sort }}'
[bar baz foo]
```
@@ -229,13 +229,6 @@ input | strings.Split separator
### Examples
-Use on its own to produce an array:
-```console
-$ gomplate -i '{{ "Bart,Lisa,Maggie" | strings.Split "," }}'
-[Bart Lisa Maggie]
-```
-
-Use in combination with `range` to iterate over all items:
```console
$ gomplate -i '{{range ("Bart,Lisa,Maggie" | strings.Split ",") }}Hello, {{.}}
{{end}}'
@@ -244,13 +237,6 @@ Hello, Lisa
Hello, Maggie
```
-Use in combination with `index` function to pick a specific value from the resulting array
-```console
-$ gomplate -i '{{index ("Bart,Lisa,Maggie" | strings.Split ",") 0 }}'
-Bart
-```
-
-
## `strings.SplitN`
Creates a slice by splitting a string on a given delimiter. The count determines
@@ -439,7 +425,7 @@ in | strings.ShellQuote
### Examples
```console
-$ gomplate -i "{{ slice \"one word\" \"foo='bar baz'\" | shellQuote }}"
+$ gomplate -i "{{ coll.Slice \"one word\" \"foo='bar baz'\" | shellQuote }}"
'one word' 'foo='"'"'bar baz'"'"''
```
```console
@@ -885,7 +871,7 @@ input | strings.RuneCount
### Examples
```console
-$ gomplate -i '{{ range (slice "\u03a9" "\u0030" "\u1430") }}{{ printf "%s is %d bytes and %d runes\n" . (len .) (strings.RuneCount .) }}{{ end }}'
+$ gomplate -i '{{ range (coll.Slice "\u03a9" "\u0030" "\u1430") }}{{ printf "%s is %d bytes and %d runes\n" . (len .) (strings.RuneCount .) }}{{ end }}'
Ω is 2 bytes and 1 runes
0 is 1 bytes and 1 runes
ᐰ is 3 bytes and 1 runes
diff --git a/docs/content/syntax.md b/docs/content/syntax.md
index 35f4c82e..277503f5 100644
--- a/docs/content/syntax.md
+++ b/docs/content/syntax.md
@@ -32,7 +32,7 @@ is rendered.
By default, every line containing an action will render a newline. For example, the action block below:
```
-{{ range slice "Foo" "bar" "baz" }}
+{{ range coll.Slice "Foo" "bar" "baz" }}
Hello, {{ . }}!
{{ end }}
```
@@ -60,7 +60,7 @@ Here are a few examples.
### Suppressing leading newlines
```
-{{- range slice "Foo" "bar" "baz" }}
+{{- range coll.Slice "Foo" "bar" "baz" }}
Hello, {{ . }}!
{{- end }}
```
@@ -79,7 +79,7 @@ Hello, baz!
This code:
```
-{{ range slice "Foo" "bar" "baz" -}}
+{{ range coll.Slice "Foo" "bar" "baz" -}}
Hello, {{ . }}!
{{ end -}}
```
@@ -97,7 +97,7 @@ Hello, baz!
This code:
```
-{{- range slice "Foo" "bar" "baz" -}}
+{{- range coll.Slice "Foo" "bar" "baz" -}}
Hello, {{ . }}!
{{- end -}}
```
diff --git a/funcs/coll.go b/funcs/coll.go
index 8c6e9f47..9be0bfc2 100644
--- a/funcs/coll.go
+++ b/funcs/coll.go
@@ -2,8 +2,11 @@ package funcs
import (
"context"
+ "reflect"
"github.com/hairyhenderson/gomplate/v3/conv"
+ "github.com/hairyhenderson/gomplate/v3/internal/deprecated"
+ "github.com/hairyhenderson/gomplate/v3/internal/texttemplate"
"github.com/hairyhenderson/gomplate/v3/coll"
"github.com/pkg/errors"
@@ -31,7 +34,7 @@ func CreateCollFuncs(ctx context.Context) map[string]interface{} {
f["coll"] = func() interface{} { return ns }
f["has"] = ns.Has
- f["slice"] = ns.Slice
+ f["slice"] = ns.deprecatedSlice
f["dict"] = ns.Dict
f["keys"] = ns.Keys
f["values"] = ns.Values
@@ -56,6 +59,18 @@ func (CollFuncs) Slice(args ...interface{}) []interface{} {
return coll.Slice(args...)
}
+// deprecatedSlice -
+// Deprecated: use coll.Slice instead
+func (f *CollFuncs) deprecatedSlice(args ...interface{}) []interface{} {
+ deprecated.WarnDeprecated(f.ctx, "the 'slice' alias for coll.Slice is deprecated - use coll.Slice instead")
+ return coll.Slice(args...)
+}
+
+// GoSlice - same as text/template's 'slice' function
+func (CollFuncs) GoSlice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) {
+ return texttemplate.GoSlice(item, indexes...)
+}
+
// Has -
func (CollFuncs) Has(in interface{}, key string) bool {
return coll.Has(in, key)
diff --git a/funcs/coll_test.go b/funcs/coll_test.go
index 9b6a0b9a..96bb9248 100644
--- a/funcs/coll_test.go
+++ b/funcs/coll_test.go
@@ -2,6 +2,7 @@ package funcs
import (
"context"
+ "reflect"
"strconv"
"testing"
@@ -142,3 +143,36 @@ func TestOmit(t *testing.T) {
assert.NoError(t, err)
assert.EqualValues(t, map[string]interface{}{}, out)
}
+
+func TestGoSlice(t *testing.T) {
+ t.Parallel()
+
+ c := &CollFuncs{}
+
+ in := reflect.ValueOf(nil)
+ _, err := c.GoSlice(in)
+ assert.Error(t, err)
+
+ in = reflect.ValueOf(42)
+ _, err = c.GoSlice(in)
+ assert.Error(t, err)
+
+ // invalid index type
+ in = reflect.ValueOf([]interface{}{1})
+ _, err = c.GoSlice(in, reflect.ValueOf([]interface{}{[]int{2}}))
+ assert.Error(t, err)
+
+ // valid slice, no slicing
+ in = reflect.ValueOf([]int{1})
+ out, err := c.GoSlice(in)
+ assert.NoError(t, err)
+ assert.Equal(t, reflect.TypeOf([]int{}), out.Type())
+ assert.EqualValues(t, []int{1}, out.Interface())
+
+ // valid slice, slicing
+ in = reflect.ValueOf([]string{"foo", "bar", "baz"})
+ out, err = c.GoSlice(in, reflect.ValueOf(1), reflect.ValueOf(3))
+ assert.NoError(t, err)
+ assert.Equal(t, reflect.TypeOf([]string{}), out.Type())
+ assert.EqualValues(t, []string{"bar", "baz"}, out.Interface())
+}
diff --git a/funcs/conv.go b/funcs/conv.go
index 823dccdf..9fc16fcc 100644
--- a/funcs/conv.go
+++ b/funcs/conv.go
@@ -61,7 +61,7 @@ func (ConvFuncs) ToBools(in ...interface{}) []bool {
}
// Slice -
-// Deprecated: use coll.Slice instead
+// Deprecated: use [coll.Slice] instead
func (f *ConvFuncs) Slice(args ...interface{}) []interface{} {
deprecated.WarnDeprecated(f.ctx, "conv.Slice is deprecated - use coll.Slice instead")
return coll.Slice(args...)
diff --git a/gomplate_test.go b/gomplate_test.go
index 19a5a865..9737ad75 100644
--- a/gomplate_test.go
+++ b/gomplate_test.go
@@ -105,17 +105,6 @@ func TestYAMLTemplates(t *testing.T) {
assert.Equal(t, "bar", testTemplate(t, g, `{{ index (yamlArray "[\"foo\",\"bar\"]") 1 }}`))
}
-func TestSliceTemplates(t *testing.T) {
- g := NewRenderer(Options{
- Funcs: template.FuncMap{
- "slice": conv.Slice,
- },
- })
- assert.Equal(t, "foo", testTemplate(t, g, `{{index (slice "foo") 0}}`))
- assert.Equal(t, `[foo bar 42]`, testTemplate(t, g, `{{slice "foo" "bar" 42}}`))
- assert.Equal(t, `helloworld`, testTemplate(t, g, `{{range slice "hello" "world"}}{{.}}{{end}}`))
-}
-
func TestHasTemplate(t *testing.T) {
g := NewRenderer(Options{
Funcs: template.FuncMap{
diff --git a/internal/tests/integration/collection_test.go b/internal/tests/integration/collection_test.go
index ccdc7a15..cc83e14d 100644
--- a/internal/tests/integration/collection_test.go
+++ b/internal/tests/integration/collection_test.go
@@ -61,9 +61,9 @@ func TestColl_Sort(t *testing.T) {
`, "foo\nbaz\nbar\n")
inOutTest(t, `
-{{- coll.Sort (slice "b" "a" "c" "aa") }}
-{{ coll.Sort (slice "b" 14 "c" "aa") }}
-{{ coll.Sort (slice 3.14 3.0 4.0) }}
+{{- coll.Sort (coll.Slice "b" "a" "c" "aa") }}
+{{ coll.Sort (coll.Slice "b" 14 "c" "aa") }}
+{{ coll.Sort (coll.Slice 3.14 3.0 4.0) }}
{{ coll.Sort "Scheme" (coll.Slice (conv.URL "zzz:///") (conv.URL "https:///") (conv.URL "http:///")) }}
`, `[a aa b c]
[b 14 c aa]
diff --git a/internal/texttemplate/exec.go b/internal/texttemplate/exec.go
new file mode 100644
index 00000000..f4906cb3
--- /dev/null
+++ b/internal/texttemplate/exec.go
@@ -0,0 +1,24 @@
+// Taken and adapted from the stdlib text/template/funcs.go.
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package texttemplate
+
+import (
+ "reflect"
+)
+
+// indirectInterface returns the concrete value in an interface value,
+// or else the zero reflect.Value.
+// That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x):
+// the fact that x was an interface value is forgotten.
+func indirectInterface(v reflect.Value) reflect.Value {
+ if v.Kind() != reflect.Interface {
+ return v
+ }
+ if v.IsNil() {
+ return reflect.Value{}
+ }
+ return v.Elem()
+}
diff --git a/internal/texttemplate/exec_test.go b/internal/texttemplate/exec_test.go
new file mode 100644
index 00000000..4696b3c4
--- /dev/null
+++ b/internal/texttemplate/exec_test.go
@@ -0,0 +1,107 @@
+// Taken and adapted from the stdlib text/template/exec_test.go.
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package texttemplate
+
+import (
+ "bytes"
+ "testing"
+ gotemplate "text/template"
+)
+
+// T has lots of interesting pieces to use to test execution.
+type T struct {
+ Tmpl *gotemplate.Template
+ Empty3 any
+ S string
+ SI []int
+ SICap []int
+ AI [3]int
+}
+
+var tVal = &T{
+ S: "xyz",
+ SI: []int{3, 4, 5},
+ SICap: make([]int, 5, 10),
+ AI: [3]int{3, 4, 5},
+ Empty3: []int{7, 8},
+ Tmpl: gotemplate.Must(gotemplate.New("").Parse("test template")),
+}
+
+//nolint:govet
+type execTest struct {
+ name string
+ input string
+ output string
+ data any
+ ok bool
+}
+
+var execTests = []execTest{
+ // Slicing.
+ {"slice[:]", "{{slice .SI}}", "[3 4 5]", tVal, true},
+ {"slice[1:]", "{{slice .SI 1}}", "[4 5]", tVal, true},
+ {"slice[1:2]", "{{slice .SI 1 2}}", "[4]", tVal, true},
+ {"slice[-1:]", "{{slice .SI -1}}", "", tVal, false},
+ {"slice[1:-2]", "{{slice .SI 1 -2}}", "", tVal, false},
+ {"slice[1:2:-1]", "{{slice .SI 1 2 -1}}", "", tVal, false},
+ {"slice[2:1]", "{{slice .SI 2 1}}", "", tVal, false},
+ {"slice[2:2:1]", "{{slice .SI 2 2 1}}", "", tVal, false},
+ {"out of range", "{{slice .SI 4 5}}", "", tVal, false},
+ {"out of range", "{{slice .SI 2 2 5}}", "", tVal, false},
+ {"len(s) < indexes < cap(s)", "{{slice .SICap 6 10}}", "[0 0 0 0]", tVal, true},
+ {"len(s) < indexes < cap(s)", "{{slice .SICap 6 10 10}}", "[0 0 0 0]", tVal, true},
+ {"indexes > cap(s)", "{{slice .SICap 10 11}}", "", tVal, false},
+ {"indexes > cap(s)", "{{slice .SICap 6 10 11}}", "", tVal, false},
+ {"array[:]", "{{slice .AI}}", "[3 4 5]", tVal, true},
+ {"array[1:]", "{{slice .AI 1}}", "[4 5]", tVal, true},
+ {"array[1:2]", "{{slice .AI 1 2}}", "[4]", tVal, true},
+ {"string[:]", "{{slice .S}}", "xyz", tVal, true},
+ {"string[0:1]", "{{slice .S 0 1}}", "x", tVal, true},
+ {"string[1:]", "{{slice .S 1}}", "yz", tVal, true},
+ {"string[1:2]", "{{slice .S 1 2}}", "y", tVal, true},
+ {"out of range", "{{slice .S 1 5}}", "", tVal, false},
+ {"3-index slice of string", "{{slice .S 1 2 2}}", "", tVal, false},
+ {"slice of an interface field", "{{slice .Empty3 0 1}}", "[7]", tVal, true},
+}
+
+func testExecute(execTests []execTest, template *gotemplate.Template, t *testing.T) {
+ b := new(bytes.Buffer)
+ funcs := gotemplate.FuncMap{"slice": GoSlice}
+
+ for _, test := range execTests {
+ var tmpl *gotemplate.Template
+ var err error
+ if template == nil {
+ tmpl, err = gotemplate.New(test.name).Funcs(funcs).Parse(test.input)
+ } else {
+ tmpl, err = template.New(test.name).Funcs(funcs).Parse(test.input)
+ }
+ if err != nil {
+ t.Errorf("%s: parse error: %s", test.name, err)
+ continue
+ }
+ b.Reset()
+ err = tmpl.Execute(b, test.data)
+ switch {
+ case !test.ok && err == nil:
+ t.Errorf("%s: expected error; got none", test.name)
+ continue
+ case test.ok && err != nil:
+ t.Errorf("%s: unexpected execute error: %s", test.name, err)
+ continue
+ case !test.ok && err != nil:
+ // expected error, got one
+ }
+ result := b.String()
+ if result != test.output {
+ t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result)
+ }
+ }
+}
+
+func TestExecute(t *testing.T) {
+ testExecute(execTests, nil, t)
+}
diff --git a/internal/texttemplate/funcs.go b/internal/texttemplate/funcs.go
new file mode 100644
index 00000000..8658af6f
--- /dev/null
+++ b/internal/texttemplate/funcs.go
@@ -0,0 +1,79 @@
+// Taken and adapted from the stdlib text/template/funcs.go.
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package texttemplate
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// indexArg checks if a reflect.Value can be used as an index, and converts it to int if possible.
+func indexArg(index reflect.Value, cap int) (int, error) {
+ var x int64
+ switch index.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ x = index.Int()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ x = int64(index.Uint())
+ case reflect.Invalid:
+ return 0, fmt.Errorf("cannot index slice/array with nil")
+ default:
+ return 0, fmt.Errorf("cannot index slice/array with type %s", index.Type())
+ }
+ if x < 0 || int(x) < 0 || int(x) > cap {
+ return 0, fmt.Errorf("index out of range: %d", x)
+ }
+ return int(x), nil
+}
+
+// Slicing.
+
+// slice returns the result of slicing its first argument by the remaining
+// arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2], while "slice x"
+// is x[:], "slice x 1" is x[1:], and "slice x 1 2 3" is x[1:2:3]. The first
+// argument must be a string, slice, or array.
+func GoSlice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) {
+ item = indirectInterface(item)
+ if !item.IsValid() {
+ return reflect.Value{}, fmt.Errorf("slice of untyped nil")
+ }
+ if len(indexes) > 3 {
+ return reflect.Value{}, fmt.Errorf("too many slice indexes: %d", len(indexes))
+ }
+ var cap int
+ switch item.Kind() {
+ case reflect.String:
+ if len(indexes) == 3 {
+ return reflect.Value{}, fmt.Errorf("cannot 3-index slice a string")
+ }
+ cap = item.Len()
+ case reflect.Array, reflect.Slice:
+ cap = item.Cap()
+ default:
+ return reflect.Value{}, fmt.Errorf("can't slice item of type %s", item.Type())
+ }
+ // set default values for cases item[:], item[i:].
+ idx := [3]int{0, item.Len()}
+ for i, index := range indexes {
+ x, err := indexArg(index, cap)
+ if err != nil {
+ return reflect.Value{}, err
+ }
+ idx[i] = x
+ }
+ // given item[i:j], make sure i <= j.
+ if idx[0] > idx[1] {
+ return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[0], idx[1])
+ }
+ if len(indexes) < 3 {
+ return item.Slice(idx[0], idx[1]), nil
+ }
+ // given item[i:j:k], make sure i <= j <= k.
+ if idx[1] > idx[2] {
+ return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[1], idx[2])
+ }
+ return item.Slice3(idx[0], idx[1], idx[2]), nil
+}