diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-09-09 11:15:11 +0300 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-09-10 10:19:01 +0300 |
commit | fe6676c775b8d917a661238f24fd4a9088f25d50 (patch) | |
tree | a84c05a70dc203392db4343a2185cfe0a5714140 /tpl | |
parent | 7a97d3e6bca1e30826e1662b5f299b66aa8ab385 (diff) |
tpl/collections: Improve type handling in collections.Slice
Fixes #5188
Diffstat (limited to 'tpl')
-rw-r--r-- | tpl/collections/collections.go | 44 | ||||
-rw-r--r-- | tpl/collections/collections_test.go | 37 | ||||
-rw-r--r-- | tpl/collections/reflect_helpers.go | 17 |
3 files changed, 79 insertions, 19 deletions
diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index 98d62a62c..99257040b 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -319,18 +319,10 @@ func (ns *Namespace) Group(key interface{}, items interface{}) (interface{}, err return nil, errors.New("nil is not a valid key to group by") } - tp := reflect.TypeOf(items) - switch tp.Kind() { - case reflect.Array, reflect.Slice: - tp = tp.Elem() - if tp.Kind() == reflect.Ptr { - tp = tp.Elem() - } - in := reflect.New(tp).Interface() - switch vv := in.(type) { - case collections.Grouper: - return vv.Group(key, items) - } + in := newSliceElement(items) + + if g, ok := in.(collections.Grouper); ok { + return g.Group(key, items) } return nil, fmt.Errorf("grouping not supported for type %T", items) @@ -514,7 +506,33 @@ func (ns *Namespace) Shuffle(seq interface{}) (interface{}, error) { } // Slice returns a slice of all passed arguments. -func (ns *Namespace) Slice(args ...interface{}) []interface{} { +func (ns *Namespace) Slice(args ...interface{}) interface{} { + if len(args) == 0 { + return args + } + + first := args[0] + allTheSame := true + if len(args) > 1 { + // This can be a mix of types. + firstType := reflect.TypeOf(first) + for i := 1; i < len(args); i++ { + if firstType != reflect.TypeOf(args[i]) { + allTheSame = false + break + } + } + } + + if allTheSame { + if g, ok := first.(collections.Slicer); ok { + v, err := g.Slice(args) + if err == nil { + return v + } + } + } + return args } diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go index 07fc4afe6..a02128f37 100644 --- a/tpl/collections/collections_test.go +++ b/tpl/collections/collections_test.go @@ -25,6 +25,7 @@ import ( "testing" "time" + "github.com/gohugoio/hugo/common/collections" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/helpers" @@ -110,6 +111,8 @@ func TestGroup(t *testing.T) { {"b", []tstGrouper2{tstGrouper2{}, tstGrouper2{}}, "b(2)"}, {"a", []*tstGrouper{}, "a(0)"}, {"a", []string{"a", "b"}, false}, + {"a", "asdf", false}, + {"a", nil, false}, {nil, []*tstGrouper{&tstGrouper{}, &tstGrouper{}}, false}, } { errMsg := fmt.Sprintf("[%d] %v", i, test) @@ -633,25 +636,47 @@ func TestShuffleRandomising(t *testing.T) { } } +var _ collections.Slicer = (*tstSlicer)(nil) + +type tstSlicer struct { + name string +} + +func (p *tstSlicer) Slice(items []interface{}) (interface{}, error) { + result := make(tstSlicers, len(items)) + for i, v := range items { + result[i] = v.(*tstSlicer) + } + return result, nil +} + +type tstSlicers []*tstSlicer + func TestSlice(t *testing.T) { t.Parallel() ns := New(&deps.Deps{}) for i, test := range []struct { - args []interface{} + args []interface{} + expected interface{} }{ - {[]interface{}{"a", "b"}}, - // errors - {[]interface{}{5, "b"}}, - {[]interface{}{tstNoStringer{}}}, + {[]interface{}{"a", "b"}, []interface{}{"a", "b"}}, + {[]interface{}{&tstSlicer{"a"}, &tstSlicer{"b"}}, tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}}, + {[]interface{}{&tstSlicer{"a"}, "b"}, []interface{}{&tstSlicer{"a"}, "b"}}, + {[]interface{}{}, []interface{}{}}, + {[]interface{}{nil}, []interface{}{nil}}, + {[]interface{}{5, "b"}, []interface{}{5, "b"}}, + {[]interface{}{tstNoStringer{}}, []interface{}{tstNoStringer{}}}, } { errMsg := fmt.Sprintf("[%d] %v", i, test.args) result := ns.Slice(test.args...) - assert.Equal(t, test.args, result, errMsg) + assert.Equal(t, test.expected, result, errMsg) } + + assert.Len(t, ns.Slice(), 0) } func TestUnion(t *testing.T) { diff --git a/tpl/collections/reflect_helpers.go b/tpl/collections/reflect_helpers.go index 69eaa68c4..643a0a7e5 100644 --- a/tpl/collections/reflect_helpers.go +++ b/tpl/collections/reflect_helpers.go @@ -102,6 +102,23 @@ func convertNumber(v reflect.Value, to reflect.Kind) (reflect.Value, error) { } +func newSliceElement(items interface{}) interface{} { + tp := reflect.TypeOf(items) + if tp == nil { + return nil + } + switch tp.Kind() { + case reflect.Array, reflect.Slice: + tp = tp.Elem() + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + + return reflect.New(tp).Interface() + } + return nil +} + func isNumber(kind reflect.Kind) bool { return isInt(kind) || isUint(kind) || isFloat(kind) } |