common/collections: Allow a mix of slice types in append/Scratch.Add
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 27 Oct 2018 09:10:39 +0000 (11:10 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 27 Oct 2018 13:05:40 +0000 (15:05 +0200)
The type handling in these was improved in Hugo 0.49, but this also meant that it was no longer possible to start out with a string slice and later append `Page` etc. to it.

This commit makes sure that the old behaviour is now possible again by falling back to a `[]interface{}` as a last resort.

Fixes #5361

common/collections/append.go
common/collections/append_test.go
common/maps/scratch_test.go

index 97bf849889649555a60ea2c56cefa3e25a9a0769..617d6258a1ab986d7a2c6751cc1cadd09a2e9324 100644 (file)
@@ -48,6 +48,10 @@ func Append(to interface{}, from ...interface{}) (interface{}, error) {
                                // If we get []string []string, we append the from slice to to
                                if tot == fromt {
                                        return reflect.AppendSlice(tov, fromv).Interface(), nil
+                               } else if !fromt.AssignableTo(tot) {
+                                       // Fall back to a []interface{} slice.
+                                       return appendToInterfaceSliceFromValues(tov, fromv)
+
                                }
                        }
                }
@@ -60,7 +64,8 @@ func Append(to interface{}, from ...interface{}) (interface{}, error) {
        for _, f := range from {
                fv := reflect.ValueOf(f)
                if !fv.Type().AssignableTo(tot) {
-                       return nil, fmt.Errorf("append element type mismatch: expected %v, got %v", tot, fv.Type())
+                       // Fall back to a []interface{} slice.
+                       return appendToInterfaceSlice(tov, from...)
                }
                tov = reflect.Append(tov, fv)
        }
@@ -68,6 +73,32 @@ func Append(to interface{}, from ...interface{}) (interface{}, error) {
        return tov.Interface(), nil
 }
 
+func appendToInterfaceSliceFromValues(slice1, slice2 reflect.Value) ([]interface{}, error) {
+       var tos []interface{}
+
+       for _, slice := range []reflect.Value{slice1, slice2} {
+               for i := 0; i < slice.Len(); i++ {
+                       tos = append(tos, slice.Index(i).Interface())
+               }
+       }
+
+       return tos, nil
+}
+
+func appendToInterfaceSlice(tov reflect.Value, from ...interface{}) ([]interface{}, error) {
+       var tos []interface{}
+
+       for i := 0; i < tov.Len(); i++ {
+               tos = append(tos, tov.Index(i).Interface())
+       }
+
+       for _, v := range from {
+               tos = append(tos, v)
+       }
+
+       return tos, nil
+}
+
 // indirect is borrowed from the Go stdlib: 'text/template/exec.go'
 // TODO(bep) consolidate
 func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
index f89ec60f0b72b5b7df28318b6fc0f9c2d652ca06..c08a69c0d896811f7f760cfe02002b3ce7db2cb4 100644 (file)
@@ -46,10 +46,12 @@ func TestAppend(t *testing.T) {
                {testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}},
                        []interface{}{&tstSlicerIn1{"c"}},
                        testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}, &tstSlicerIn1{"c"}}},
+               //https://github.com/gohugoio/hugo/issues/5361
+               {[]string{"a", "b"}, []interface{}{tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
+                       []interface{}{"a", "b", &tstSlicer{"a"}, &tstSlicer{"b"}}},
+               {[]string{"a", "b"}, []interface{}{&tstSlicer{"a"}},
+                       []interface{}{"a", "b", &tstSlicer{"a"}}},
                // Errors
-               {testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}},
-                       []interface{}{"c"},
-                       false},
                {"", []interface{}{[]string{"a", "b"}}, false},
                // No string concatenation.
                {"ab",
index bf37d79dfb5247cea7fed310e412cb7b0860e6b8..4550a22c53279ceec93559c4b9afcb9f5cc4f2ff 100644 (file)
@@ -96,6 +96,20 @@ func TestScratchAddTypedSliceToInterfaceSlice(t *testing.T) {
 
 }
 
+// https://github.com/gohugoio/hugo/issues/5361
+func TestScratchAddDifferentTypedSliceToInterfaceSlice(t *testing.T) {
+       t.Parallel()
+       assert := require.New(t)
+
+       scratch := NewScratch()
+       scratch.Set("slice", []string{"foo"})
+
+       _, err := scratch.Add("slice", []int{1, 2})
+       assert.NoError(err)
+       assert.Equal([]interface{}{"foo", 1, 2}, scratch.Get("slice"))
+
+}
+
 func TestScratchSet(t *testing.T) {
        t.Parallel()
        assert := require.New(t)