hugo

Unnamed repository; edit this file 'description' to name the repository.

git clone git://git.shimmy1996.com/hugo.git
commit c46fc838a9320adfc6532b1b543e903c48b3b4cb
parent 504c78da4b5020e1fd13a1195ad38a9e85f8289a
Author: Ujjwal Goyal <importujjwal@gmail.com>
Date:   Sun,  7 Mar 2021 00:08:10 +0530

tpl: Allow 'Querify' to take lone slice/interface argument

Querify can now take a lone string/interface slice (with string
keys) as a parameter, or multiple string parameters, to build
URL queries.

Querify earlier used 'Dictionary' to add key/value pairs to a
map to build URL queries. Changed to dynamically generate ordered
key/value pairs. Cannot take string slice as key (earlier
possible due to Dictionary).

Added tests and benchmarks for querify.

Closes #6735

Diffstat:
Mtpl/collections/collections.go | 36+++++++++++++++++++++++++++++++-----
Mtpl/collections/collections_test.go | 33+++++++++++++++++++++++++++++++++
Mtpl/collections/init.go | 4++++
3 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go
@@ -424,13 +424,39 @@ func (ns *Namespace) Last(limit interface{}, seq interface{}) (interface{}, erro
 // Querify encodes the given parameters in URL-encoded form ("bar=baz&foo=quux") sorted by key.
 func (ns *Namespace) Querify(params ...interface{}) (string, error) {
 	qs := url.Values{}
-	vals, err := ns.Dictionary(params...)
-	if err != nil {
-		return "", errors.New("querify keys must be strings")
+
+	if len(params) == 1 {
+		switch v := params[0].(type) {
+		case []string:
+			if len(v)%2 != 0 {
+				return "", errors.New("invalid query")
+			}
+
+			for i := 0; i < len(v); i += 2 {
+				qs.Add(v[i], v[i+1])
+			}
+
+			return qs.Encode(), nil
+
+		case []interface{}:
+			params = v
+
+		default:
+			return "", errors.New("query keys must be strings")
+		}
+	}
+
+	if len(params)%2 != 0 {
+		return "", errors.New("invalid query")
 	}
 
-	for name, value := range vals {
-		qs.Add(name, fmt.Sprintf("%v", value))
+	for i := 0; i < len(params); i += 2 {
+		switch v := params[i].(type) {
+		case string:
+			qs.Add(v, fmt.Sprintf("%v", params[i+1]))
+		default:
+			return "", errors.New("query keys must be strings")
+		}
 	}
 
 	return qs.Encode(), nil
diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go
@@ -564,9 +564,16 @@ func TestQuerify(t *testing.T) {
 	}{
 		{[]interface{}{"a", "b"}, "a=b"},
 		{[]interface{}{"a", "b", "c", "d", "f", " &"}, `a=b&c=d&f=+%26`},
+		{[]interface{}{[]string{"a", "b"}}, "a=b"},
+		{[]interface{}{[]string{"a", "b", "c", "d", "f", " &"}}, `a=b&c=d&f=+%26`},
+		{[]interface{}{[]interface{}{"x", "y"}}, `x=y`},
+		{[]interface{}{[]interface{}{"x", 5}}, `x=5`},
 		// errors
 		{[]interface{}{5, "b"}, false},
 		{[]interface{}{"a", "b", "c"}, false},
+		{[]interface{}{[]string{"a", "b", "c"}}, false},
+		{[]interface{}{[]string{"a", "b"}, "c"}, false},
+		{[]interface{}{[]interface{}{"c", "d", "e"}}, false},
 	} {
 		errMsg := qt.Commentf("[%d] %v", i, test.params)
 
@@ -582,6 +589,32 @@ func TestQuerify(t *testing.T) {
 	}
 }
 
+func BenchmarkQuerify(b *testing.B) {
+	ns := New(&deps.Deps{})
+	params := []interface{}{"a", "b", "c", "d", "f", " &"}
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		_, err := ns.Querify(params...)
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+func BenchmarkQuerifySlice(b *testing.B) {
+	ns := New(&deps.Deps{})
+	params := []string{"a", "b", "c", "d", "f", " &"}
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		_, err := ns.Querify(params)
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
 func TestSeq(t *testing.T) {
 	t.Parallel()
 	c := qt.New(t)
diff --git a/tpl/collections/init.go b/tpl/collections/init.go
@@ -122,6 +122,10 @@ func init() {
 					`<a href="https://www.google.com?{{ (querify "q" "test" "page" 3) | safeURL }}">Search</a>`,
 					`<a href="https://www.google.com?page=3&amp;q=test">Search</a>`,
 				},
+				{
+					`{{ slice "foo" 1 "bar" 2 | querify | safeHTML }}`,
+					`bar=2&foo=1`,
+				},
 			},
 		)