shithub: hugo

Download patch

ref: dac7092a9cb22d59db28fb15af15f7b14ff47588
parent: b27ccf34bf4e5ee618a66fa11c68a9690e395034
author: Bjørn Erik Pedersen <[email protected]>
date: Sat Oct 27 07:10:39 EDT 2018

common/collections: Allow a mix of slice types in append/Scratch.Add

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

--- a/common/collections/append.go
+++ b/common/collections/append.go
@@ -48,6 +48,10 @@
 				// 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,12 +64,39 @@
 	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)
 	}
 
 	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'
--- a/common/collections/append_test.go
+++ b/common/collections/append_test.go
@@ -46,10 +46,12 @@
 		{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",
--- a/common/maps/scratch_test.go
+++ b/common/maps/scratch_test.go
@@ -96,6 +96,20 @@
 
 }
 
+// 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)