shithub: hugo

Download patch

ref: d7a67dcb51829b12d492d3f2ee4f6e2a3834da63
parent: 3e421bd47cd35061df89c1c127ec8fa4ae368449
author: Bjørn Erik Pedersen <[email protected]>
date: Thu Apr 18 06:27:23 EDT 2019

tpl/collections: Make Pages etc. work in uniq

Fixes #5852

--- a/tpl/collections/collections.go
+++ b/tpl/collections/collections.go
@@ -16,7 +16,6 @@
 package collections
 
 import (
-	"errors"
 	"fmt"
 	"html/template"
 	"math/rand"
@@ -30,6 +29,7 @@
 	"github.com/gohugoio/hugo/common/types"
 	"github.com/gohugoio/hugo/deps"
 	"github.com/gohugoio/hugo/helpers"
+	"github.com/pkg/errors"
 	"github.com/spf13/cast"
 )
 
@@ -655,40 +655,38 @@
 
 // Uniq takes in a slice or array and returns a slice with subsequent
 // duplicate elements removed.
-func (ns *Namespace) Uniq(l interface{}) (interface{}, error) {
-	if l == nil {
+func (ns *Namespace) Uniq(seq interface{}) (interface{}, error) {
+	if seq == nil {
 		return make([]interface{}, 0), nil
 	}
 
-	lv := reflect.ValueOf(l)
-	lv, isNil := indirect(lv)
-	if isNil {
-		return nil, errors.New("invalid nil argument to Uniq")
-	}
+	v := reflect.ValueOf(seq)
+	var slice reflect.Value
 
-	var ret reflect.Value
-
-	switch lv.Kind() {
+	switch v.Kind() {
 	case reflect.Slice:
-		ret = reflect.MakeSlice(lv.Type(), 0, 0)
+		slice = reflect.MakeSlice(v.Type(), 0, 0)
 	case reflect.Array:
-		ret = reflect.MakeSlice(reflect.SliceOf(lv.Type().Elem()), 0, 0)
+		slice = reflect.MakeSlice(reflect.SliceOf(v.Type().Elem()), 0, 0)
 	default:
-		return nil, errors.New("Can't use Uniq on " + reflect.ValueOf(lv).Type().String())
+		return nil, errors.Errorf("type %T not supported", seq)
 	}
 
-	for i := 0; i != lv.Len(); i++ {
-		lvv := lv.Index(i)
-		lvv, isNil := indirect(lvv)
-		if isNil {
-			continue
+	seen := make(map[interface{}]bool)
+	for i := 0; i < v.Len(); i++ {
+		ev, _ := indirectInterface(v.Index(i))
+		if !ev.Type().Comparable() {
+			return nil, errors.New("elements must be comparable")
 		}
-
-		if !ns.In(ret.Interface(), lvv.Interface()) {
-			ret = reflect.Append(ret, lvv)
+		key := normalize(ev)
+		if _, found := seen[key]; !found {
+			slice = reflect.Append(slice, ev)
+			seen[key] = true
 		}
 	}
-	return ret.Interface(), nil
+
+	return slice.Interface(), nil
+
 }
 
 // KeyVals creates a key and values wrapper.
--- a/tpl/collections/collections_test.go
+++ b/tpl/collections/collections_test.go
@@ -322,23 +322,23 @@
 type pagesPtr []*testPage
 type pagesVals []testPage
 
+var (
+	p1 = &testPage{"A"}
+	p2 = &testPage{"B"}
+	p3 = &testPage{"C"}
+	p4 = &testPage{"D"}
+
+	p1v = testPage{"A"}
+	p2v = testPage{"B"}
+	p3v = testPage{"C"}
+	p4v = testPage{"D"}
+)
+
 func TestIntersect(t *testing.T) {
 	t.Parallel()
 
 	ns := New(&deps.Deps{})
 
-	var (
-		p1 = &testPage{"A"}
-		p2 = &testPage{"B"}
-		p3 = &testPage{"C"}
-		p4 = &testPage{"D"}
-
-		p1v = testPage{"A"}
-		p2v = testPage{"B"}
-		p3v = testPage{"C"}
-		p4v = testPage{"D"}
-	)
-
 	for i, test := range []struct {
 		l1, l2 interface{}
 		expect interface{}
@@ -671,18 +671,6 @@
 
 	ns := New(&deps.Deps{})
 
-	var (
-		p1 = &testPage{"A"}
-		p2 = &testPage{"B"}
-		//		p3 = &page{"C"}
-		p4 = &testPage{"D"}
-
-		p1v = testPage{"A"}
-		//p2v = page{"B"}
-		p3v = testPage{"C"}
-		//p4v = page{"D"}
-	)
-
 	for i, test := range []struct {
 		l1     interface{}
 		l2     interface{}
@@ -786,7 +774,15 @@
 		{[]int{1, 2, 3, 2}, []int{1, 2, 3}, false},
 		{[4]int{1, 2, 3, 2}, []int{1, 2, 3}, false},
 		{nil, make([]interface{}, 0), false},
-		// should-errors
+		// Pointers
+		{pagesPtr{p1, p2, p3, p2}, pagesPtr{p1, p2, p3}, false},
+		{pagesPtr{}, pagesPtr{}, false},
+		// Structs
+		{pagesVals{p3v, p2v, p3v, p2v}, pagesVals{p3v, p2v}, false},
+
+		// should fail
+		// uncomparable types
+		{[]map[string]int{{"K1": 1}}, []map[string]int{{"K2": 2}, {"K2": 2}}, true},
 		{1, 1, true},
 		{"foo", "fo", true},
 	} {