diff options
author | Bjørn Erik Pedersen <[email protected]> | 2018-10-08 10:25:15 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2018-10-08 12:30:50 +0200 |
commit | 31a8bb8c071c6f2ca8cbd73057912932a1e7e943 (patch) | |
tree | 698f8d82cf1dc2bb6c56ba3a615ef4b441361c6a /tpl/collections | |
parent | 8e825ddf5b31ce5fe570c78ac7a78c8056fb60f9 (diff) | |
download | hugo-31a8bb8c071c6f2ca8cbd73057912932a1e7e943.tar.gz hugo-31a8bb8c071c6f2ca8cbd73057912932a1e7e943.zip |
common/maps: Improve append in Scratch
This commit consolidates the reflective collections handling in `.Scratch` vs the `tpl` package so they use the same code paths.
This commit also adds support for a corner case where a typed slice is appended to a nil or empty `[]interface{}`.
Fixes #5275
Diffstat (limited to 'tpl/collections')
-rw-r--r-- | tpl/collections/append.go | 42 | ||||
-rw-r--r-- | tpl/collections/append_test.go | 13 | ||||
-rw-r--r-- | tpl/collections/apply.go | 2 | ||||
-rw-r--r-- | tpl/collections/collections.go | 34 | ||||
-rw-r--r-- | tpl/collections/collections_test.go | 83 |
5 files changed, 7 insertions, 167 deletions
diff --git a/tpl/collections/append.go b/tpl/collections/append.go index 20afa0e72..297328dc8 100644 --- a/tpl/collections/append.go +++ b/tpl/collections/append.go @@ -15,8 +15,8 @@ package collections import ( "errors" - "fmt" - "reflect" + + "github.com/gohugoio/hugo/common/collections" ) // Append appends the arguments up to the last one to the slice in the last argument. @@ -33,42 +33,6 @@ func (ns *Namespace) Append(args ...interface{}) (interface{}, error) { to := args[len(args)-1] from := args[:len(args)-1] - tov, toIsNil := indirect(reflect.ValueOf(to)) - - toIsNil = toIsNil || to == nil - var tot reflect.Type - - if !toIsNil { - if tov.Kind() != reflect.Slice { - return nil, fmt.Errorf("expected a slice, got %T", to) - } - - tot = tov.Type().Elem() - toIsNil = tov.Len() == 0 - - if len(from) == 1 { - // If we get []string []string, we append the from slice to to - fromv := reflect.ValueOf(from[0]) - if fromv.Kind() == reflect.Slice { - fromt := reflect.TypeOf(from[0]).Elem() - if tot == fromt { - return reflect.AppendSlice(tov, fromv).Interface(), nil - } - } - } - } - - if toIsNil { - return ns.Slice(from...), nil - } - - for _, f := range from { - fv := reflect.ValueOf(f) - if tot != fv.Type() { - return nil, fmt.Errorf("append element type mismatch: expected %v, got %v", tot, fv.Type()) - } - tov = reflect.Append(tov, fv) - } + return collections.Append(to, from...) - return tov.Interface(), nil } diff --git a/tpl/collections/append_test.go b/tpl/collections/append_test.go index b0a751fb8..f886aca22 100644 --- a/tpl/collections/append_test.go +++ b/tpl/collections/append_test.go @@ -18,11 +18,11 @@ import ( "reflect" "testing" - "github.com/alecthomas/assert" "github.com/gohugoio/hugo/deps" "github.com/stretchr/testify/require" ) +// Also see tests in common/collection. func TestAppend(t *testing.T) { t.Parallel() @@ -36,16 +36,6 @@ func TestAppend(t *testing.T) { {[]string{"a", "b"}, []interface{}{"c"}, []string{"a", "b", "c"}}, {[]string{"a", "b"}, []interface{}{"c", "d", "e"}, []string{"a", "b", "c", "d", "e"}}, {[]string{"a", "b"}, []interface{}{[]string{"c", "d", "e"}}, []string{"a", "b", "c", "d", "e"}}, - {nil, []interface{}{"a", "b"}, []string{"a", "b"}}, - {nil, []interface{}{nil}, []interface{}{nil}}, - {tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}, - []interface{}{&tstSlicer{"c"}}, - tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}, &tstSlicer{"c"}}}, - {&tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}, - []interface{}{&tstSlicer{"c"}}, - tstSlicers{&tstSlicer{"a"}, - &tstSlicer{"b"}, - &tstSlicer{"c"}}}, // Errors {"", []interface{}{[]string{"a", "b"}}, false}, {[]string{"a", "b"}, []interface{}{}, false}, @@ -73,5 +63,4 @@ func TestAppend(t *testing.T) { } } - assert.Len(t, ns.Slice(), 0) } diff --git a/tpl/collections/apply.go b/tpl/collections/apply.go index 0b2b00621..d715aeb00 100644 --- a/tpl/collections/apply.go +++ b/tpl/collections/apply.go @@ -136,7 +136,7 @@ func (ns *Namespace) lookupFunc(fname string) (reflect.Value, bool) { return m, true } -// indirect is taken from 'text/template/exec.go' +// indirect is borrowed from the Go stdlib: 'text/template/exec.go' func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { if v.IsNil() { diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index 6d11d75f4..d9baddf6b 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -523,39 +523,7 @@ func (ns *Namespace) Slice(args ...interface{}) interface{} { return args } - first := args[0] - firstType := reflect.TypeOf(first) - - if firstType == nil { - return args - } - - if g, ok := first.(collections.Slicer); ok { - v, err := g.Slice(args) - if err == nil { - return v - } - - // If Slice fails, the items are not of the same type and - // []interface{} is the best we can do. - return args - } - - if len(args) > 1 { - // This can be a mix of types. - for i := 1; i < len(args); i++ { - if firstType != reflect.TypeOf(args[i]) { - // []interface{} is the best we can do - return args - } - } - } - - slice := reflect.MakeSlice(reflect.SliceOf(firstType), len(args), len(args)) - for i, arg := range args { - slice.Index(i).Set(reflect.ValueOf(arg)) - } - return slice.Interface() + return collections.Slice(args...) } type intersector struct { diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go index dc8929f39..c8a7207ea 100644 --- a/tpl/collections/collections_test.go +++ b/tpl/collections/collections_test.go @@ -25,7 +25,6 @@ import ( "testing" "time" - "github.com/gohugoio/hugo/common/collections" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/helpers" @@ -644,83 +643,7 @@ func TestShuffleRandomising(t *testing.T) { } } -var _ collections.Slicer = (*tstSlicer)(nil) -var _ collections.Slicer = (*tstSlicerIn1)(nil) -var _ collections.Slicer = (*tstSlicerIn2)(nil) -var _ testSlicerInterface = (*tstSlicerIn1)(nil) -var _ testSlicerInterface = (*tstSlicerIn1)(nil) - -type testSlicerInterface interface { - Name() string -} - -type testSlicerInterfaces []testSlicerInterface - -type tstSlicerIn1 struct { - name string -} - -type tstSlicerIn2 struct { - name string -} - -type tstSlicer struct { - name string -} - -func (p *tstSlicerIn1) Slice(in interface{}) (interface{}, error) { - items := in.([]interface{}) - result := make(testSlicerInterfaces, len(items)) - for i, v := range items { - switch vv := v.(type) { - case testSlicerInterface: - result[i] = vv - default: - return nil, errors.New("invalid type") - } - - } - return result, nil -} - -func (p *tstSlicerIn2) Slice(in interface{}) (interface{}, error) { - items := in.([]interface{}) - result := make(testSlicerInterfaces, len(items)) - for i, v := range items { - switch vv := v.(type) { - case testSlicerInterface: - result[i] = vv - default: - return nil, errors.New("invalid type") - } - } - return result, nil -} - -func (p *tstSlicerIn1) Name() string { - return p.Name() -} - -func (p *tstSlicerIn2) Name() string { - return p.Name() -} - -func (p *tstSlicer) Slice(in interface{}) (interface{}, error) { - items := in.([]interface{}) - result := make(tstSlicers, len(items)) - for i, v := range items { - switch vv := v.(type) { - case *tstSlicer: - result[i] = vv - default: - return nil, errors.New("invalid type") - } - } - return result, nil -} - -type tstSlicers []*tstSlicer - +// Also see tests in commons/collection. func TestSlice(t *testing.T) { t.Parallel() @@ -731,14 +654,10 @@ func TestSlice(t *testing.T) { expected interface{} }{ {[]interface{}{"a", "b"}, []string{"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{}}, []tstNoStringer{tstNoStringer{}}}, - {[]interface{}{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}, testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn2{"b"}}}, - {[]interface{}{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}, []interface{}{&tstSlicerIn1{"a"}, &tstSlicer{"b"}}}, } { errMsg := fmt.Sprintf("[%d] %v", i, test.args) |