Skip to content

Commit

Permalink
Add coll.GoSlice and deprecate slice alias
Browse files Browse the repository at this point in the history
Signed-off-by: Dave Henderson <[email protected]>
  • Loading branch information
hairyhenderson committed Sep 25, 2022
1 parent ea83b8f commit 33f2537
Show file tree
Hide file tree
Showing 12 changed files with 349 additions and 21 deletions.
44 changes: 37 additions & 7 deletions docs-src/content/functions/coll.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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: |
Expand All @@ -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"}'
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -257,7 +287,7 @@ 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) }}'
Expand Down
8 changes: 4 additions & 4 deletions docs-src/content/functions/conv.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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"}'
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs-src/content/functions/func_doc.md.tmpl
Original file line number Diff line number Diff line change
@@ -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}}
Expand Down
4 changes: 2 additions & 2 deletions docs-src/content/functions/strings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -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\" }}"
Expand Down
43 changes: 41 additions & 2 deletions docs/content/functions/coll.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ Hello world!
Hello everybody!
```

## `coll.Slice`
## `coll.Slice` _(deprecated)_
**Deprecation Notice:** `slice` alias is deprecated, use `coll.Slice` instead

**Alias:** `slice`

Expand All @@ -81,7 +82,45 @@ 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.
```

### 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 |

### Examples

```console
$ gomplate -i '{{ range coll.Slice "Bart" "Lisa" "Maggie" }}Hello, {{ . }}{{ end }}'
Hello, Bart
Hello, Lisa
Hello, Maggie
Expand Down
17 changes: 16 additions & 1 deletion funcs/coll.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
34 changes: 34 additions & 0 deletions funcs/coll_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package funcs

import (
"context"
"reflect"
"strconv"
"testing"

Expand Down Expand Up @@ -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())
}
2 changes: 1 addition & 1 deletion funcs/conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
Expand Down
6 changes: 3 additions & 3 deletions internal/tests/integration/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
24 changes: 24 additions & 0 deletions internal/texttemplate/exec.go
Original file line number Diff line number Diff line change
@@ -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()
}
Loading

0 comments on commit 33f2537

Please sign in to comment.