diff options
| -rw-r--r-- | coll/coll.go | 32 | ||||
| -rw-r--r-- | conv/conv.go | 18 | ||||
| -rw-r--r-- | funcs/random.go | 19 | ||||
| -rw-r--r-- | internal/conv/conv.go | 29 | ||||
| -rw-r--r-- | internal/conv/conv_test.go | 49 |
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) + } + }) + } +} |
