summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coll/coll.go32
-rw-r--r--conv/conv.go18
-rw-r--r--funcs/random.go19
-rw-r--r--internal/conv/conv.go29
-rw-r--r--internal/conv/conv_test.go49
5 files changed, 91 insertions, 56 deletions
diff --git a/coll/coll.go b/coll/coll.go
index 8e41ed91..dbbbea8c 100644
--- a/coll/coll.go
+++ b/coll/coll.go
@@ -11,7 +11,7 @@ import (
"sort"
"github.com/hairyhenderson/gomplate/conv"
- "github.com/pkg/errors"
+ iconv "github.com/hairyhenderson/gomplate/internal/conv"
)
// Slice creates a slice from a bunch of arguments
@@ -19,21 +19,6 @@ func Slice(args ...interface{}) []interface{} {
return args
}
-func interfaceSlice(slice interface{}) ([]interface{}, error) {
- s := reflect.ValueOf(slice)
- kind := s.Kind()
- switch kind {
- case reflect.Slice, reflect.Array:
- ret := make([]interface{}, s.Len())
- for i := 0; i < s.Len(); i++ {
- ret[i] = s.Index(i).Interface()
- }
- return ret, nil
- default:
- return nil, errors.Errorf("expected an array or slice, but got a %T", s)
- }
-}
-
// Has determines whether or not a given object has a property with the given key
func Has(in interface{}, key interface{}) bool {
av := reflect.ValueOf(in)
@@ -120,7 +105,7 @@ func Values(in ...map[string]interface{}) ([]interface{}, error) {
// Append v to the end of list. No matter what type of input slice or array list is, a new []interface{} is always returned.
func Append(v interface{}, list interface{}) ([]interface{}, error) {
- l, err := interfaceSlice(list)
+ l, err := iconv.InterfaceSlice(list)
if err != nil {
return nil, err
}
@@ -130,7 +115,7 @@ func Append(v interface{}, list interface{}) ([]interface{}, error) {
// Prepend v to the beginning of list. No matter what type of input slice or array list is, a new []interface{} is always returned.
func Prepend(v interface{}, list interface{}) ([]interface{}, error) {
- l, err := interfaceSlice(list)
+ l, err := iconv.InterfaceSlice(list)
if err != nil {
return nil, err
}
@@ -140,7 +125,7 @@ func Prepend(v interface{}, list interface{}) ([]interface{}, error) {
// Uniq finds the unique values within list. No matter what type of input slice or array list is, a new []interface{} is always returned.
func Uniq(list interface{}) ([]interface{}, error) {
- l, err := interfaceSlice(list)
+ l, err := iconv.InterfaceSlice(list)
if err != nil {
return nil, err
}
@@ -156,7 +141,7 @@ func Uniq(list interface{}) ([]interface{}, error) {
// Reverse the list. No matter what type of input slice or array list is, a new []interface{} is always returned.
func Reverse(list interface{}) ([]interface{}, error) {
- l, err := interfaceSlice(list)
+ l, err := iconv.InterfaceSlice(list)
if err != nil {
return nil, err
}
@@ -224,7 +209,7 @@ func Sort(key string, list interface{}) (out []interface{}, err error) {
return nil, nil
}
- ia, err := interfaceSlice(list)
+ ia, err := iconv.InterfaceSlice(list)
if err != nil {
return nil, err
}
@@ -296,14 +281,15 @@ func sameTypes(a []interface{}) bool {
// Flatten a nested array or slice to at most 'depth' levels. Use depth of -1
// to completely flatten the input.
// Returns a new slice without modifying the input.
-func Flatten(list interface{}, depth int) (out []interface{}, err error) {
- l, err := interfaceSlice(list)
+func Flatten(list interface{}, depth int) ([]interface{}, error) {
+ l, err := iconv.InterfaceSlice(list)
if err != nil {
return nil, err
}
if depth == 0 {
return l, nil
}
+ out := make([]interface{}, 0, len(l)*2)
for _, v := range l {
s := reflect.ValueOf(v)
kind := s.Kind()
diff --git a/conv/conv.go b/conv/conv.go
index cd9cb771..20435f93 100644
--- a/conv/conv.go
+++ b/conv/conv.go
@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
+ iconv "github.com/hairyhenderson/gomplate/internal/conv"
"github.com/pkg/errors"
)
@@ -82,7 +83,7 @@ func Join(in interface{}, sep string) (out string, err error) {
var a []interface{}
a, ok = in.([]interface{})
if !ok {
- a, err = interfaceSlice(in)
+ a, err = iconv.InterfaceSlice(in)
if err != nil {
return "", errors.Wrap(err, "Input to Join must be an array")
}
@@ -99,21 +100,6 @@ func Join(in interface{}, sep string) (out string, err error) {
return "", errors.New("Input to Join must be an array")
}
-func interfaceSlice(slice interface{}) ([]interface{}, error) {
- s := reflect.ValueOf(slice)
- kind := s.Kind()
- switch kind {
- case reflect.Slice, reflect.Array:
- ret := make([]interface{}, s.Len())
- for i := 0; i < s.Len(); i++ {
- ret[i] = s.Index(i).Interface()
- }
- return ret, nil
- default:
- return nil, errors.Errorf("expected an array or slice, but got a %T", s)
- }
-}
-
// Has determines whether or not a given object has a property with the given key
func Has(in interface{}, key interface{}) bool {
av := reflect.ValueOf(in)
diff --git a/funcs/random.go b/funcs/random.go
index d4dce3e7..0e3e7a0c 100644
--- a/funcs/random.go
+++ b/funcs/random.go
@@ -1,12 +1,12 @@
package funcs
import (
- "reflect"
"strconv"
"sync"
"unicode/utf8"
"github.com/hairyhenderson/gomplate/conv"
+ iconv "github.com/hairyhenderson/gomplate/internal/conv"
"github.com/hairyhenderson/gomplate/random"
"github.com/pkg/errors"
)
@@ -107,24 +107,9 @@ func toCodePoints(l, u string) (rune, rune, error) {
return rune(li), rune(ui), nil
}
-func interfaceSlice(slice interface{}) ([]interface{}, error) {
- s := reflect.ValueOf(slice)
- kind := s.Kind()
- switch kind {
- case reflect.Slice, reflect.Array:
- ret := make([]interface{}, s.Len())
- for i := 0; i < s.Len(); i++ {
- ret[i] = s.Index(i).Interface()
- }
- return ret, nil
- default:
- return nil, errors.Errorf("expected an array or slice, but got a %T", s)
- }
-}
-
// Item -
func (f *RandomFuncs) Item(items interface{}) (interface{}, error) {
- i, err := interfaceSlice(items)
+ i, err := iconv.InterfaceSlice(items)
if err != nil {
return nil, err
}
diff --git a/internal/conv/conv.go b/internal/conv/conv.go
new file mode 100644
index 00000000..d953296c
--- /dev/null
+++ b/internal/conv/conv.go
@@ -0,0 +1,29 @@
+package conv
+
+import (
+ "reflect"
+
+ "github.com/pkg/errors"
+)
+
+// InterfaceSlice converts an array or slice of any type into an []interface{}
+// for use in functions that expect this.
+func InterfaceSlice(slice interface{}) ([]interface{}, error) {
+ // avoid all this nonsense if this is already a []interface{}...
+ if s, ok := slice.([]interface{}); ok {
+ return s, nil
+ }
+ s := reflect.ValueOf(slice)
+ kind := s.Kind()
+ switch kind {
+ case reflect.Slice, reflect.Array:
+ l := s.Len()
+ ret := make([]interface{}, l)
+ for i := 0; i < l; i++ {
+ ret[i] = s.Index(i).Interface()
+ }
+ return ret, nil
+ default:
+ return nil, errors.Errorf("expected an array or slice, but got a %T", s)
+ }
+}
diff --git a/internal/conv/conv_test.go b/internal/conv/conv_test.go
new file mode 100644
index 00000000..c83c8dd0
--- /dev/null
+++ b/internal/conv/conv_test.go
@@ -0,0 +1,49 @@
+package conv
+
+import (
+ "fmt"
+ "testing"
+
+ "gotest.tools/v3/assert"
+)
+
+func TestInterfaceSlice(t *testing.T) {
+ data := []struct {
+ in, expected interface{}
+ }{
+ {[]int{1, 2, 3}, []interface{}{1, 2, 3}},
+ {[3]int{1, 2, 3}, []interface{}{1, 2, 3}},
+ {[]string{"foo", "bar", "baz"}, []interface{}{"foo", "bar", "baz"}},
+ {[3]string{"foo", "bar", "baz"}, []interface{}{"foo", "bar", "baz"}},
+ {[]interface{}{[]string{}, []int{1, 2}, 3}, []interface{}{[]string{}, []int{1, 2}, 3}},
+ {[3]interface{}{[]string{}, []int{1, 2}, 3}, []interface{}{[]string{}, []int{1, 2}, 3}},
+ }
+
+ for _, d := range data {
+ out, err := InterfaceSlice(d.in)
+ assert.NilError(t, err)
+ assert.DeepEqual(t, d.expected, out)
+ }
+
+ _, err := InterfaceSlice(42)
+ assert.ErrorContains(t, err, "")
+}
+
+func BenchmarkInterfaceSlice(b *testing.B) {
+ data := []interface{}{
+ []int{1, 2, 3},
+ [3]int{1, 2, 3},
+ []string{"foo", "bar", "baz", "foo", "bar", "baz", "foo", "bar", "baz", "foo", "bar", "baz"},
+ [12]string{"foo", "bar", "baz", "foo", "bar", "baz", "foo", "bar", "baz", "foo", "bar", "baz"},
+ []interface{}{[]string{}, []int{1, 2}, 3},
+ [3]interface{}{[]string{}, []int{1, 2}, 3},
+ }
+
+ for _, d := range data {
+ b.Run(fmt.Sprintf("%T(%v)", d, d), func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ InterfaceSlice(d)
+ }
+ })
+ }
+}