Skip to content

Commit

Permalink
tpl/collections: Fix append when appending a slice to a slice of slices
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Jun 14, 2023
1 parent 35e9b3e commit 666950d
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 3 deletions.
23 changes: 20 additions & 3 deletions common/collections/append.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import (
// If length of from is one and the only element is a slice of same type as to,
// it will be appended.
func Append(to any, from ...any) (any, error) {
if len(from) == 0 {
return to, nil
}
tov, toIsNil := indirect(reflect.ValueOf(to))

toIsNil = toIsNil || to == nil
Expand All @@ -32,26 +35,40 @@ func Append(to any, from ...any) (any, error) {
return nil, fmt.Errorf("expected a slice, got %T", to)
}

fromv := reflect.ValueOf(from[0])
fromt := fromv.Type()
if fromt.Kind() == reflect.Slice {
fromt = fromt.Elem()
}

tot = tov.Type().Elem()
if tot.Kind() == reflect.Slice {
totvt := tot.Elem()
if totvt != fromt {
return nil, fmt.Errorf("cannot append slice of %s to slice of %s", fromt, totvt)
} else {
return reflect.Append(tov, fromv).Interface(), nil
}
}

toIsNil = tov.Len() == 0

if len(from) == 1 {
fromv := reflect.ValueOf(from[0])

if fromv.Kind() == reflect.Slice {
if toIsNil {
// If we get nil []string, we just return the []string
return from[0], nil
}

fromt := reflect.TypeOf(from[0]).Elem()

// 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 Down
33 changes: 33 additions & 0 deletions common/collections/append_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,36 @@ func TestAppend(t *testing.T) {
c.Assert(result, qt.DeepEquals, test.expected)
}
}

// #11093
func TestAppendToMultiDimensionalSlice(t *testing.T) {
t.Parallel()
c := qt.New(t)

for _, test := range []struct {
to any
from any
expected any
}{
{[][]string{{"a", "b"}},
[]string{"c", "d"},
[][]string{
{"a", "b"},
{"c", "d"},
},
},
{[][]string{{"a", "b"}},
[]int{1, 2},
false,
},
} {
result, err := Append(test.to, test.from)
if b, ok := test.expected.(bool); ok && !b {
c.Assert(err, qt.Not(qt.IsNil))
} else {
c.Assert(err, qt.IsNil)
c.Assert(result, qt.DeepEquals, test.expected)
}
}

}
31 changes: 31 additions & 0 deletions tpl/collections/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,34 @@ Desc: [map[a:3 b:3] map[a:3 b:1] map[a:3 b:1] map[a:3 b:1] map[a:3 b:0] map[a:3

}
}

// Issue #11004.
func TestAppendSliceToASliceOfSlices(t *testing.T) {
t.Parallel()

files := `
-- hugo.toml --
-- layouts/index.html --
{{ $obj := slice (slice "a") }}
{{ $obj = $obj | append (slice "b") }}
{{ $obj = $obj | append (slice "c") }}
{{ $obj }}
`

for i := 0; i < 4; i++ {

b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()

b.AssertFileContent("public/index.html", "[[a] [b] [c]]")

}

}

0 comments on commit 666950d

Please sign in to comment.