Skip to content

Commit

Permalink
common/collections: Allow a mix of slice types in append/Scratch.Add
Browse files Browse the repository at this point in the history
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 gohugoio#5361
  • Loading branch information
bep committed Oct 27, 2018
1 parent b27ccf3 commit dac7092
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
33 changes: 32 additions & 1 deletion common/collections/append.go
Expand Up @@ -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)

}
}
}
Expand All @@ -60,14 +64,41 @@ 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)
}

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) {
Expand Down
8 changes: 5 additions & 3 deletions common/collections/append_test.go
Expand Up @@ -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",
Expand Down
14 changes: 14 additions & 0 deletions common/maps/scratch_test.go
Expand Up @@ -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)
Expand Down

0 comments on commit dac7092

Please sign in to comment.