shithub: hugo

Download patch

ref: f1c29b017bbd88e701cd5151dd186e868672ef89
parent: 93b3b1386714999d716e03b131f77234248f1724
author: Cameron Moore <[email protected]>
date: Mon May 1 17:25:39 EDT 2017

tpl/collections: Add support for interfaces to intersect

Fixes #1952

--- a/tpl/collections/collections.go
+++ b/tpl/collections/collections.go
@@ -298,21 +298,49 @@
 					l2vv := l2v.Index(j)
 					switch l1vv.Kind() {
 					case reflect.String:
-						if l1vv.Type() == l2vv.Type() && l1vv.String() == l2vv.String() && !ns.In(r.Interface(), l2vv.Interface()) {
-							r = reflect.Append(r, l2vv)
+						l2t, err := toString(l2vv)
+						if err == nil && l1vv.String() == l2t && !ns.In(r.Interface(), l1vv.Interface()) {
+							r = reflect.Append(r, l1vv)
 						}
 					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-						switch l2vv.Kind() {
-						case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-							if l1vv.Int() == l2vv.Int() && !ns.In(r.Interface(), l2vv.Interface()) {
-								r = reflect.Append(r, l2vv)
-							}
+						l2t, err := toInt(l2vv)
+						if err == nil && l1vv.Int() == l2t && !ns.In(r.Interface(), l1vv.Interface()) {
+							r = reflect.Append(r, l1vv)
 						}
 					case reflect.Float32, reflect.Float64:
-						switch l2vv.Kind() {
-						case reflect.Float32, reflect.Float64:
-							if l1vv.Float() == l2vv.Float() && !ns.In(r.Interface(), l2vv.Interface()) {
-								r = reflect.Append(r, l2vv)
+						l2t, err := toFloat(l2vv)
+						if err == nil && l1vv.Float() == l2t && !ns.In(r.Interface(), l1vv.Interface()) {
+							r = reflect.Append(r, l1vv)
+						}
+					case reflect.Interface:
+						switch l1vvActual := l1vv.Interface().(type) {
+						case string:
+							switch l2vvActual := l2vv.Interface().(type) {
+							case string:
+								if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
+									r = reflect.Append(r, l1vv)
+								}
+							}
+						case int, int8, int16, int32, int64:
+							switch l2vvActual := l2vv.Interface().(type) {
+							case int, int8, int16, int32, int64:
+								if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
+									r = reflect.Append(r, l1vv)
+								}
+							}
+						case uint, uint8, uint16, uint32, uint64:
+							switch l2vvActual := l2vv.Interface().(type) {
+							case uint, uint8, uint16, uint32, uint64:
+								if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
+									r = reflect.Append(r, l1vv)
+								}
+							}
+						case float32, float64:
+							switch l2vvActual := l2vv.Interface().(type) {
+							case float32, float64:
+								if l1vvActual == l2vvActual && !ns.In(r.Interface(), l1vvActual) {
+									r = reflect.Append(r, l1vv)
+								}
 							}
 						}
 					}
--- a/tpl/collections/collections_test.go
+++ b/tpl/collections/collections_test.go
@@ -260,7 +260,9 @@
 		{[]string{"a", "b"}, []string{"a", "b", "c"}, []string{"a", "b"}},
 		{[]string{"a", "b", "c"}, []string{"d", "e"}, []string{}},
 		{[]string{}, []string{}, []string{}},
-		{nil, nil, make([]interface{}, 0)},
+		{[]string{"a", "b"}, nil, []interface{}{}},
+		{nil, []string{"a", "b"}, []interface{}{}},
+		{nil, nil, []interface{}{}},
 		{[]string{"1", "2"}, []int{1, 2}, []string{}},
 		{[]int{1, 2}, []string{"1", "2"}, []int{}},
 		{[]int{1, 2, 4}, []int{2, 4}, []int{2, 4}},
@@ -270,6 +272,36 @@
 		// errors
 		{"not array or slice", []string{"a"}, false},
 		{[]string{"a"}, "not array or slice", false},
+
+		// []interface{} ∩ []interface{}
+		{[]interface{}{"a", "b", "c"}, []interface{}{"a", "b", "b"}, []interface{}{"a", "b"}},
+		{[]interface{}{1, 2, 3}, []interface{}{1, 2, 2}, []interface{}{1, 2}},
+		{[]interface{}{int8(1), int8(2), int8(3)}, []interface{}{int8(1), int8(2), int8(2)}, []interface{}{int8(1), int8(2)}},
+		{[]interface{}{int16(1), int16(2), int16(3)}, []interface{}{int16(1), int16(2), int16(2)}, []interface{}{int16(1), int16(2)}},
+		{[]interface{}{int32(1), int32(2), int32(3)}, []interface{}{int32(1), int32(2), int32(2)}, []interface{}{int32(1), int32(2)}},
+		{[]interface{}{int64(1), int64(2), int64(3)}, []interface{}{int64(1), int64(2), int64(2)}, []interface{}{int64(1), int64(2)}},
+		{[]interface{}{float32(1), float32(2), float32(3)}, []interface{}{float32(1), float32(2), float32(2)}, []interface{}{float32(1), float32(2)}},
+		{[]interface{}{float64(1), float64(2), float64(3)}, []interface{}{float64(1), float64(2), float64(2)}, []interface{}{float64(1), float64(2)}},
+
+		// []interface{} ∩ []T
+		{[]interface{}{"a", "b", "c"}, []string{"a", "b", "b"}, []interface{}{"a", "b"}},
+		{[]interface{}{1, 2, 3}, []int{1, 2, 2}, []interface{}{1, 2}},
+		{[]interface{}{int8(1), int8(2), int8(3)}, []int8{1, 2, 2}, []interface{}{int8(1), int8(2)}},
+		{[]interface{}{int16(1), int16(2), int16(3)}, []int16{1, 2, 2}, []interface{}{int16(1), int16(2)}},
+		{[]interface{}{int32(1), int32(2), int32(3)}, []int32{1, 2, 2}, []interface{}{int32(1), int32(2)}},
+		{[]interface{}{int64(1), int64(2), int64(3)}, []int64{1, 2, 2}, []interface{}{int64(1), int64(2)}},
+		{[]interface{}{float32(1), float32(2), float32(3)}, []float32{1, 2, 2}, []interface{}{float32(1), float32(2)}},
+		{[]interface{}{float64(1), float64(2), float64(3)}, []float64{1, 2, 2}, []interface{}{float64(1), float64(2)}},
+
+		// []T ∩ []interface{}
+		{[]string{"a", "b", "c"}, []interface{}{"a", "b", "b"}, []string{"a", "b"}},
+		{[]int{1, 2, 3}, []interface{}{1, 2, 2}, []int{1, 2}},
+		{[]int8{1, 2, 3}, []interface{}{int8(1), int8(2), int8(2)}, []int8{1, 2}},
+		{[]int16{1, 2, 3}, []interface{}{int16(1), int16(2), int16(2)}, []int16{1, 2}},
+		{[]int32{1, 2, 3}, []interface{}{int32(1), int32(2), int32(2)}, []int32{1, 2}},
+		{[]int64{1, 2, 3}, []interface{}{int64(1), int64(2), int64(2)}, []int64{1, 2}},
+		{[]float32{1, 2, 3}, []interface{}{float32(1), float32(2), float32(2)}, []float32{1, 2}},
+		{[]float64{1, 2, 3}, []interface{}{float64(1), float64(2), float64(2)}, []float64{1, 2}},
 	} {
 		errMsg := fmt.Sprintf("[%d] %v", i, test)
 
--- a/tpl/collections/where.go
+++ b/tpl/collections/where.go
@@ -124,16 +124,15 @@
 			iv := v.Int()
 			ivp = &iv
 			for i := 0; i < mv.Len(); i++ {
-				if anInt := toInt(mv.Index(i)); anInt != -1 {
+				if anInt, err := toInt(mv.Index(i)); err == nil {
 					ima = append(ima, anInt)
 				}
-
 			}
 		case reflect.String:
 			sv := v.String()
 			svp = &sv
 			for i := 0; i < mv.Len(); i++ {
-				if aString := toString(mv.Index(i)); aString != "" {
+				if aString, err := toString(mv.Index(i)); err == nil {
 					sma = append(sma, aString)
 				}
 			}
@@ -382,26 +381,37 @@
 	return rv.Interface(), nil
 }
 
+// toFloat returns the int value if possible.
+func toFloat(v reflect.Value) (float64, error) {
+	switch v.Kind() {
+	case reflect.Float32, reflect.Float64:
+		return v.Float(), nil
+	case reflect.Interface:
+		return toFloat(v.Elem())
+	}
+	return -1, errors.New("unable to convert value to float")
+}
+
 // toInt returns the int value if possible, -1 if not.
-func toInt(v reflect.Value) int64 {
+func toInt(v reflect.Value) (int64, error) {
 	switch v.Kind() {
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		return v.Int()
+		return v.Int(), nil
 	case reflect.Interface:
 		return toInt(v.Elem())
 	}
-	return -1
+	return -1, errors.New("unable to convert value to int")
 }
 
 // toString returns the string value if possible, "" if not.
-func toString(v reflect.Value) string {
+func toString(v reflect.Value) (string, error) {
 	switch v.Kind() {
 	case reflect.String:
-		return v.String()
+		return v.String(), nil
 	case reflect.Interface:
 		return toString(v.Elem())
 	}
-	return ""
+	return "", errors.New("unable to convert value to string")
 }
 
 var (