diff options
8 files changed, 786 insertions, 0 deletions
diff --git a/vendor/github.com/google/go-querystring/.gitignore b/vendor/github.com/google/go-querystring/.gitignore new file mode 100644 index 0000000..9ed3b07 --- /dev/null +++ b/vendor/github.com/google/go-querystring/.gitignore | |||
@@ -0,0 +1 @@ | |||
*.test | |||
diff --git a/vendor/github.com/google/go-querystring/.travis.yml b/vendor/github.com/google/go-querystring/.travis.yml new file mode 100644 index 0000000..881d087 --- /dev/null +++ b/vendor/github.com/google/go-querystring/.travis.yml | |||
@@ -0,0 +1,5 @@ | |||
1 | language: go | ||
2 | |||
3 | go: | ||
4 | - 1.x | ||
5 | - 1.11.x | ||
diff --git a/vendor/github.com/google/go-querystring/CONTRIBUTING.md b/vendor/github.com/google/go-querystring/CONTRIBUTING.md new file mode 100644 index 0000000..51cf5cd --- /dev/null +++ b/vendor/github.com/google/go-querystring/CONTRIBUTING.md | |||
@@ -0,0 +1,67 @@ | |||
1 | # How to contribute # | ||
2 | |||
3 | We'd love to accept your patches and contributions to this project. There are | ||
4 | a just a few small guidelines you need to follow. | ||
5 | |||
6 | |||
7 | ## Contributor License Agreement ## | ||
8 | |||
9 | Contributions to any Google project must be accompanied by a Contributor | ||
10 | License Agreement. This is not a copyright **assignment**, it simply gives | ||
11 | Google permission to use and redistribute your contributions as part of the | ||
12 | project. | ||
13 | |||
14 | * If you are an individual writing original source code and you're sure you | ||
15 | own the intellectual property, then you'll need to sign an [individual | ||
16 | CLA][]. | ||
17 | |||
18 | * If you work for a company that wants to allow you to contribute your work, | ||
19 | then you'll need to sign a [corporate CLA][]. | ||
20 | |||
21 | You generally only need to submit a CLA once, so if you've already submitted | ||
22 | one (even if it was for a different project), you probably don't need to do it | ||
23 | again. | ||
24 | |||
25 | [individual CLA]: https://developers.google.com/open-source/cla/individual | ||
26 | [corporate CLA]: https://developers.google.com/open-source/cla/corporate | ||
27 | |||
28 | |||
29 | ## Submitting a patch ## | ||
30 | |||
31 | 1. It's generally best to start by opening a new issue describing the bug or | ||
32 | feature you're intending to fix. Even if you think it's relatively minor, | ||
33 | it's helpful to know what people are working on. Mention in the initial | ||
34 | issue that you are planning to work on that bug or feature so that it can | ||
35 | be assigned to you. | ||
36 | |||
37 | 1. Follow the normal process of [forking][] the project, and setup a new | ||
38 | branch to work in. It's important that each group of changes be done in | ||
39 | separate branches in order to ensure that a pull request only includes the | ||
40 | commits related to that bug or feature. | ||
41 | |||
42 | 1. Go makes it very simple to ensure properly formatted code, so always run | ||
43 | `go fmt` on your code before committing it. You should also run | ||
44 | [golint][] over your code. As noted in the [golint readme][], it's not | ||
45 | strictly necessary that your code be completely "lint-free", but this will | ||
46 | help you find common style issues. | ||
47 | |||
48 | 1. Any significant changes should almost always be accompanied by tests. The | ||
49 | project already has good test coverage, so look at some of the existing | ||
50 | tests if you're unsure how to go about it. [gocov][] and [gocov-html][] | ||
51 | are invaluable tools for seeing which parts of your code aren't being | ||
52 | exercised by your tests. | ||
53 | |||
54 | 1. Do your best to have [well-formed commit messages][] for each change. | ||
55 | This provides consistency throughout the project, and ensures that commit | ||
56 | messages are able to be formatted properly by various git tools. | ||
57 | |||
58 | 1. Finally, push the commits to your fork and submit a [pull request][]. | ||
59 | |||
60 | [forking]: https://help.github.com/articles/fork-a-repo | ||
61 | [golint]: https://github.com/golang/lint | ||
62 | [golint readme]: https://github.com/golang/lint/blob/master/README | ||
63 | [gocov]: https://github.com/axw/gocov | ||
64 | [gocov-html]: https://github.com/matm/gocov-html | ||
65 | [well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html | ||
66 | [squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits | ||
67 | [pull request]: https://help.github.com/articles/creating-a-pull-request | ||
diff --git a/vendor/github.com/google/go-querystring/LICENSE b/vendor/github.com/google/go-querystring/LICENSE new file mode 100644 index 0000000..ae121a1 --- /dev/null +++ b/vendor/github.com/google/go-querystring/LICENSE | |||
@@ -0,0 +1,27 @@ | |||
1 | Copyright (c) 2013 Google. All rights reserved. | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are | ||
5 | met: | ||
6 | |||
7 | * Redistributions of source code must retain the above copyright | ||
8 | notice, this list of conditions and the following disclaimer. | ||
9 | * Redistributions in binary form must reproduce the above | ||
10 | copyright notice, this list of conditions and the following disclaimer | ||
11 | in the documentation and/or other materials provided with the | ||
12 | distribution. | ||
13 | * Neither the name of Google Inc. nor the names of its | ||
14 | contributors may be used to endorse or promote products derived from | ||
15 | this software without specific prior written permission. | ||
16 | |||
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
diff --git a/vendor/github.com/google/go-querystring/README.md b/vendor/github.com/google/go-querystring/README.md new file mode 100644 index 0000000..0e600be --- /dev/null +++ b/vendor/github.com/google/go-querystring/README.md | |||
@@ -0,0 +1,37 @@ | |||
1 | # go-querystring # | ||
2 | |||
3 | [![GoDoc](https://godoc.org/github.com/google/go-querystring/query?status.svg)](https://godoc.org/github.com/google/go-querystring/query) [![Build Status](https://travis-ci.org/google/go-querystring.svg?branch=master)](https://travis-ci.org/google/go-querystring) | ||
4 | |||
5 | go-querystring is Go library for encoding structs into URL query parameters. | ||
6 | |||
7 | ## Usage ## | ||
8 | |||
9 | ```go | ||
10 | import "github.com/google/go-querystring/query" | ||
11 | ``` | ||
12 | |||
13 | go-querystring is designed to assist in scenarios where you want to construct a | ||
14 | URL using a struct that represents the URL query parameters. You might do this | ||
15 | to enforce the type safety of your parameters, for example, as is done in the | ||
16 | [go-github][] library. | ||
17 | |||
18 | The query package exports a single `Values()` function. A simple example: | ||
19 | |||
20 | ```go | ||
21 | type Options struct { | ||
22 | Query string `url:"q"` | ||
23 | ShowAll bool `url:"all"` | ||
24 | Page int `url:"page"` | ||
25 | } | ||
26 | |||
27 | opt := Options{ "foo", true, 2 } | ||
28 | v, _ := query.Values(opt) | ||
29 | fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" | ||
30 | ``` | ||
31 | |||
32 | [go-github]: https://github.com/google/go-github/commit/994f6f8405f052a117d2d0b500054341048fbb08 | ||
33 | |||
34 | ## License ## | ||
35 | |||
36 | This library is distributed under the BSD-style license found in the [LICENSE](./LICENSE) | ||
37 | file. | ||
diff --git a/vendor/github.com/google/go-querystring/go.mod b/vendor/github.com/google/go-querystring/go.mod new file mode 100644 index 0000000..45dca2d --- /dev/null +++ b/vendor/github.com/google/go-querystring/go.mod | |||
@@ -0,0 +1 @@ | |||
module github.com/google/go-querystring | |||
diff --git a/vendor/github.com/google/go-querystring/query/encode.go b/vendor/github.com/google/go-querystring/query/encode.go new file mode 100644 index 0000000..37080b1 --- /dev/null +++ b/vendor/github.com/google/go-querystring/query/encode.go | |||
@@ -0,0 +1,320 @@ | |||
1 | // Copyright 2013 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | // Package query implements encoding of structs into URL query parameters. | ||
6 | // | ||
7 | // As a simple example: | ||
8 | // | ||
9 | // type Options struct { | ||
10 | // Query string `url:"q"` | ||
11 | // ShowAll bool `url:"all"` | ||
12 | // Page int `url:"page"` | ||
13 | // } | ||
14 | // | ||
15 | // opt := Options{ "foo", true, 2 } | ||
16 | // v, _ := query.Values(opt) | ||
17 | // fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" | ||
18 | // | ||
19 | // The exact mapping between Go values and url.Values is described in the | ||
20 | // documentation for the Values() function. | ||
21 | package query | ||
22 | |||
23 | import ( | ||
24 | "bytes" | ||
25 | "fmt" | ||
26 | "net/url" | ||
27 | "reflect" | ||
28 | "strconv" | ||
29 | "strings" | ||
30 | "time" | ||
31 | ) | ||
32 | |||
33 | var timeType = reflect.TypeOf(time.Time{}) | ||
34 | |||
35 | var encoderType = reflect.TypeOf(new(Encoder)).Elem() | ||
36 | |||
37 | // Encoder is an interface implemented by any type that wishes to encode | ||
38 | // itself into URL values in a non-standard way. | ||
39 | type Encoder interface { | ||
40 | EncodeValues(key string, v *url.Values) error | ||
41 | } | ||
42 | |||
43 | // Values returns the url.Values encoding of v. | ||
44 | // | ||
45 | // Values expects to be passed a struct, and traverses it recursively using the | ||
46 | // following encoding rules. | ||
47 | // | ||
48 | // Each exported struct field is encoded as a URL parameter unless | ||
49 | // | ||
50 | // - the field's tag is "-", or | ||
51 | // - the field is empty and its tag specifies the "omitempty" option | ||
52 | // | ||
53 | // The empty values are false, 0, any nil pointer or interface value, any array | ||
54 | // slice, map, or string of length zero, and any time.Time that returns true | ||
55 | // for IsZero(). | ||
56 | // | ||
57 | // The URL parameter name defaults to the struct field name but can be | ||
58 | // specified in the struct field's tag value. The "url" key in the struct | ||
59 | // field's tag value is the key name, followed by an optional comma and | ||
60 | // options. For example: | ||
61 | // | ||
62 | // // Field is ignored by this package. | ||
63 | // Field int `url:"-"` | ||
64 | // | ||
65 | // // Field appears as URL parameter "myName". | ||
66 | // Field int `url:"myName"` | ||
67 | // | ||
68 | // // Field appears as URL parameter "myName" and the field is omitted if | ||
69 | // // its value is empty | ||
70 | // Field int `url:"myName,omitempty"` | ||
71 | // | ||
72 | // // Field appears as URL parameter "Field" (the default), but the field | ||
73 | // // is skipped if empty. Note the leading comma. | ||
74 | // Field int `url:",omitempty"` | ||
75 | // | ||
76 | // For encoding individual field values, the following type-dependent rules | ||
77 | // apply: | ||
78 | // | ||
79 | // Boolean values default to encoding as the strings "true" or "false". | ||
80 | // Including the "int" option signals that the field should be encoded as the | ||
81 | // strings "1" or "0". | ||
82 | // | ||
83 | // time.Time values default to encoding as RFC3339 timestamps. Including the | ||
84 | // "unix" option signals that the field should be encoded as a Unix time (see | ||
85 | // time.Unix()) | ||
86 | // | ||
87 | // Slice and Array values default to encoding as multiple URL values of the | ||
88 | // same name. Including the "comma" option signals that the field should be | ||
89 | // encoded as a single comma-delimited value. Including the "space" option | ||
90 | // similarly encodes the value as a single space-delimited string. Including | ||
91 | // the "semicolon" option will encode the value as a semicolon-delimited string. | ||
92 | // Including the "brackets" option signals that the multiple URL values should | ||
93 | // have "[]" appended to the value name. "numbered" will append a number to | ||
94 | // the end of each incidence of the value name, example: | ||
95 | // name0=value0&name1=value1, etc. | ||
96 | // | ||
97 | // Anonymous struct fields are usually encoded as if their inner exported | ||
98 | // fields were fields in the outer struct, subject to the standard Go | ||
99 | // visibility rules. An anonymous struct field with a name given in its URL | ||
100 | // tag is treated as having that name, rather than being anonymous. | ||
101 | // | ||
102 | // Non-nil pointer values are encoded as the value pointed to. | ||
103 | // | ||
104 | // Nested structs are encoded including parent fields in value names for | ||
105 | // scoping. e.g: | ||
106 | // | ||
107 | // "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO" | ||
108 | // | ||
109 | // All other values are encoded using their default string representation. | ||
110 | // | ||
111 | // Multiple fields that encode to the same URL parameter name will be included | ||
112 | // as multiple URL values of the same name. | ||
113 | func Values(v interface{}) (url.Values, error) { | ||
114 | values := make(url.Values) | ||
115 | val := reflect.ValueOf(v) | ||
116 | for val.Kind() == reflect.Ptr { | ||
117 | if val.IsNil() { | ||
118 | return values, nil | ||
119 | } | ||
120 | val = val.Elem() | ||
121 | } | ||
122 | |||
123 | if v == nil { | ||
124 | return values, nil | ||
125 | } | ||
126 | |||
127 | if val.Kind() != reflect.Struct { | ||
128 | return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) | ||
129 | } | ||
130 | |||
131 | err := reflectValue(values, val, "") | ||
132 | return values, err | ||
133 | } | ||
134 | |||
135 | // reflectValue populates the values parameter from the struct fields in val. | ||
136 | // Embedded structs are followed recursively (using the rules defined in the | ||
137 | // Values function documentation) breadth-first. | ||
138 | func reflectValue(values url.Values, val reflect.Value, scope string) error { | ||
139 | var embedded []reflect.Value | ||
140 | |||
141 | typ := val.Type() | ||
142 | for i := 0; i < typ.NumField(); i++ { | ||
143 | sf := typ.Field(i) | ||
144 | if sf.PkgPath != "" && !sf.Anonymous { // unexported | ||
145 | continue | ||
146 | } | ||
147 | |||
148 | sv := val.Field(i) | ||
149 | tag := sf.Tag.Get("url") | ||
150 | if tag == "-" { | ||
151 | continue | ||
152 | } | ||
153 | name, opts := parseTag(tag) | ||
154 | if name == "" { | ||
155 | if sf.Anonymous && sv.Kind() == reflect.Struct { | ||
156 | // save embedded struct for later processing | ||
157 | embedded = append(embedded, sv) | ||
158 | continue | ||
159 | } | ||
160 | |||
161 | name = sf.Name | ||
162 | } | ||
163 | |||
164 | if scope != "" { | ||
165 | name = scope + "[" + name + "]" | ||
166 | } | ||
167 | |||
168 | if opts.Contains("omitempty") && isEmptyValue(sv) { | ||
169 | continue | ||
170 | } | ||
171 | |||
172 | if sv.Type().Implements(encoderType) { | ||
173 | if !reflect.Indirect(sv).IsValid() { | ||
174 | sv = reflect.New(sv.Type().Elem()) | ||
175 | } | ||
176 | |||
177 | m := sv.Interface().(Encoder) | ||
178 | if err := m.EncodeValues(name, &values); err != nil { | ||
179 | return err | ||
180 | } | ||
181 | continue | ||
182 | } | ||
183 | |||
184 | if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { | ||
185 | var del byte | ||
186 | if opts.Contains("comma") { | ||
187 | del = ',' | ||
188 | } else if opts.Contains("space") { | ||
189 | del = ' ' | ||
190 | } else if opts.Contains("semicolon") { | ||
191 | del = ';' | ||
192 | } else if opts.Contains("brackets") { | ||
193 | name = name + "[]" | ||
194 | } | ||
195 | |||
196 | if del != 0 { | ||
197 | s := new(bytes.Buffer) | ||
198 | first := true | ||
199 | for i := 0; i < sv.Len(); i++ { | ||
200 | if first { | ||
201 | first = false | ||
202 | } else { | ||
203 | s.WriteByte(del) | ||
204 | } | ||
205 | s.WriteString(valueString(sv.Index(i), opts)) | ||
206 | } | ||
207 | values.Add(name, s.String()) | ||
208 | } else { | ||
209 | for i := 0; i < sv.Len(); i++ { | ||
210 | k := name | ||
211 | if opts.Contains("numbered") { | ||
212 | k = fmt.Sprintf("%s%d", name, i) | ||
213 | } | ||
214 | values.Add(k, valueString(sv.Index(i), opts)) | ||
215 | } | ||
216 | } | ||
217 | continue | ||
218 | } | ||
219 | |||
220 | for sv.Kind() == reflect.Ptr { | ||
221 | if sv.IsNil() { | ||
222 | break | ||
223 | } | ||
224 | sv = sv.Elem() | ||
225 | } | ||
226 | |||
227 | if sv.Type() == timeType { | ||
228 | values.Add(name, valueString(sv, opts)) | ||
229 | continue | ||
230 | } | ||
231 | |||
232 | if sv.Kind() == reflect.Struct { | ||
233 | reflectValue(values, sv, name) | ||
234 | continue | ||
235 | } | ||
236 | |||
237 | values.Add(name, valueString(sv, opts)) | ||
238 | } | ||
239 | |||
240 | for _, f := range embedded { | ||
241 | if err := reflectValue(values, f, scope); err != nil { | ||
242 | return err | ||
243 | } | ||
244 | } | ||
245 | |||
246 | return nil | ||
247 | } | ||
248 | |||
249 | // valueString returns the string representation of a value. | ||
250 | func valueString(v reflect.Value, opts tagOptions) string { | ||
251 | for v.Kind() == reflect.Ptr { | ||
252 | if v.IsNil() { | ||
253 | return "" | ||
254 | } | ||
255 | v = v.Elem() | ||
256 | } | ||
257 | |||
258 | if v.Kind() == reflect.Bool && opts.Contains("int") { | ||
259 | if v.Bool() { | ||
260 | return "1" | ||
261 | } | ||
262 | return "0" | ||
263 | } | ||
264 | |||
265 | if v.Type() == timeType { | ||
266 | t := v.Interface().(time.Time) | ||
267 | if opts.Contains("unix") { | ||
268 | return strconv.FormatInt(t.Unix(), 10) | ||
269 | } | ||
270 | return t.Format(time.RFC3339) | ||
271 | } | ||
272 | |||
273 | return fmt.Sprint(v.Interface()) | ||
274 | } | ||
275 | |||
276 | // isEmptyValue checks if a value should be considered empty for the purposes | ||
277 | // of omitting fields with the "omitempty" option. | ||
278 | func isEmptyValue(v reflect.Value) bool { | ||
279 | switch v.Kind() { | ||
280 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: | ||
281 | return v.Len() == 0 | ||
282 | case reflect.Bool: | ||
283 | return !v.Bool() | ||
284 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
285 | return v.Int() == 0 | ||
286 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
287 | return v.Uint() == 0 | ||
288 | case reflect.Float32, reflect.Float64: | ||
289 | return v.Float() == 0 | ||
290 | case reflect.Interface, reflect.Ptr: | ||
291 | return v.IsNil() | ||
292 | } | ||
293 | |||
294 | if v.Type() == timeType { | ||
295 | return v.Interface().(time.Time).IsZero() | ||
296 | } | ||
297 | |||
298 | return false | ||
299 | } | ||
300 | |||
301 | // tagOptions is the string following a comma in a struct field's "url" tag, or | ||
302 | // the empty string. It does not include the leading comma. | ||
303 | type tagOptions []string | ||
304 | |||
305 | // parseTag splits a struct field's url tag into its name and comma-separated | ||
306 | // options. | ||
307 | func parseTag(tag string) (string, tagOptions) { | ||
308 | s := strings.Split(tag, ",") | ||
309 | return s[0], s[1:] | ||
310 | } | ||
311 | |||
312 | // Contains checks whether the tagOptions contains the specified option. | ||
313 | func (o tagOptions) Contains(option string) bool { | ||
314 | for _, s := range o { | ||
315 | if s == option { | ||
316 | return true | ||
317 | } | ||
318 | } | ||
319 | return false | ||
320 | } | ||
diff --git a/vendor/github.com/google/go-querystring/query/encode_test.go b/vendor/github.com/google/go-querystring/query/encode_test.go new file mode 100644 index 0000000..77bea5a --- /dev/null +++ b/vendor/github.com/google/go-querystring/query/encode_test.go | |||
@@ -0,0 +1,328 @@ | |||
1 | // Copyright 2013 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package query | ||
6 | |||
7 | import ( | ||
8 | "fmt" | ||
9 | "net/url" | ||
10 | "reflect" | ||
11 | "testing" | ||
12 | "time" | ||
13 | ) | ||
14 | |||
15 | type Nested struct { | ||
16 | A SubNested `url:"a"` | ||
17 | B *SubNested `url:"b"` | ||
18 | Ptr *SubNested `url:"ptr,omitempty"` | ||
19 | } | ||
20 | |||
21 | type SubNested struct { | ||
22 | Value string `url:"value"` | ||
23 | } | ||
24 | |||
25 | func TestValues_types(t *testing.T) { | ||
26 | str := "string" | ||
27 | strPtr := &str | ||
28 | timeVal := time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC) | ||
29 | |||
30 | tests := []struct { | ||
31 | in interface{} | ||
32 | want url.Values | ||
33 | }{ | ||
34 | { | ||
35 | // basic primitives | ||
36 | struct { | ||
37 | A string | ||
38 | B int | ||
39 | C uint | ||
40 | D float32 | ||
41 | E bool | ||
42 | }{}, | ||
43 | url.Values{ | ||
44 | "A": {""}, | ||
45 | "B": {"0"}, | ||
46 | "C": {"0"}, | ||
47 | "D": {"0"}, | ||
48 | "E": {"false"}, | ||
49 | }, | ||
50 | }, | ||
51 | { | ||
52 | // pointers | ||
53 | struct { | ||
54 | A *string | ||
55 | B *int | ||
56 | C **string | ||
57 | D *time.Time | ||
58 | }{ | ||
59 | A: strPtr, | ||
60 | C: &strPtr, | ||
61 | D: &timeVal, | ||
62 | }, | ||
63 | url.Values{ | ||
64 | "A": {str}, | ||
65 | "B": {""}, | ||
66 | "C": {str}, | ||
67 | "D": {"2000-01-01T12:34:56Z"}, | ||
68 | }, | ||
69 | }, | ||
70 | { | ||
71 | // slices and arrays | ||
72 | struct { | ||
73 | A []string | ||
74 | B []string `url:",comma"` | ||
75 | C []string `url:",space"` | ||
76 | D [2]string | ||
77 | E [2]string `url:",comma"` | ||
78 | F [2]string `url:",space"` | ||
79 | G []*string `url:",space"` | ||
80 | H []bool `url:",int,space"` | ||
81 | I []string `url:",brackets"` | ||
82 | J []string `url:",semicolon"` | ||
83 | K []string `url:",numbered"` | ||
84 | }{ | ||
85 | A: []string{"a", "b"}, | ||
86 | B: []string{"a", "b"}, | ||
87 | C: []string{"a", "b"}, | ||
88 | D: [2]string{"a", "b"}, | ||
89 | E: [2]string{"a", "b"}, | ||
90 | F: [2]string{"a", "b"}, | ||
91 | G: []*string{&str, &str}, | ||
92 | H: []bool{true, false}, | ||
93 | I: []string{"a", "b"}, | ||
94 | J: []string{"a", "b"}, | ||
95 | K: []string{"a", "b"}, | ||
96 | }, | ||
97 | url.Values{ | ||
98 | "A": {"a", "b"}, | ||
99 | "B": {"a,b"}, | ||
100 | "C": {"a b"}, | ||
101 | "D": {"a", "b"}, | ||
102 | "E": {"a,b"}, | ||
103 | "F": {"a b"}, | ||
104 | "G": {"string string"}, | ||
105 | "H": {"1 0"}, | ||
106 | "I[]": {"a", "b"}, | ||
107 | "J": {"a;b"}, | ||
108 | "K0": {"a"}, | ||
109 | "K1": {"b"}, | ||
110 | }, | ||
111 | }, | ||
112 | { | ||
113 | // other types | ||
114 | struct { | ||
115 | A time.Time | ||
116 | B time.Time `url:",unix"` | ||
117 | C bool `url:",int"` | ||
118 | D bool `url:",int"` | ||
119 | }{ | ||
120 | A: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC), | ||
121 | B: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC), | ||
122 | C: true, | ||
123 | D: false, | ||
124 | }, | ||
125 | url.Values{ | ||
126 | "A": {"2000-01-01T12:34:56Z"}, | ||
127 | "B": {"946730096"}, | ||
128 | "C": {"1"}, | ||
129 | "D": {"0"}, | ||
130 | }, | ||
131 | }, | ||
132 | { | ||
133 | struct { | ||
134 | Nest Nested `url:"nest"` | ||
135 | }{ | ||
136 | Nested{ | ||
137 | A: SubNested{ | ||
138 | Value: "that", | ||
139 | }, | ||
140 | }, | ||
141 | }, | ||
142 | url.Values{ | ||
143 | "nest[a][value]": {"that"}, | ||
144 | "nest[b]": {""}, | ||
145 | }, | ||
146 | }, | ||
147 | { | ||
148 | struct { | ||
149 | Nest Nested `url:"nest"` | ||
150 | }{ | ||
151 | Nested{ | ||
152 | Ptr: &SubNested{ | ||
153 | Value: "that", | ||
154 | }, | ||
155 | }, | ||
156 | }, | ||
157 | url.Values{ | ||
158 | "nest[a][value]": {""}, | ||
159 | "nest[b]": {""}, | ||
160 | "nest[ptr][value]": {"that"}, | ||
161 | }, | ||
162 | }, | ||
163 | { | ||
164 | nil, | ||
165 | url.Values{}, | ||
166 | }, | ||
167 | } | ||
168 | |||
169 | for i, tt := range tests { | ||
170 | v, err := Values(tt.in) | ||
171 | if err != nil { | ||
172 | t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err) | ||
173 | } | ||
174 | |||
175 | if !reflect.DeepEqual(tt.want, v) { | ||
176 | t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want) | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | |||
181 | func TestValues_omitEmpty(t *testing.T) { | ||
182 | str := "" | ||
183 | s := struct { | ||
184 | a string | ||
185 | A string | ||
186 | B string `url:",omitempty"` | ||
187 | C string `url:"-"` | ||
188 | D string `url:"omitempty"` // actually named omitempty, not an option | ||
189 | E *string `url:",omitempty"` | ||
190 | }{E: &str} | ||
191 | |||
192 | v, err := Values(s) | ||
193 | if err != nil { | ||
194 | t.Errorf("Values(%v) returned error: %v", s, err) | ||
195 | } | ||
196 | |||
197 | want := url.Values{ | ||
198 | "A": {""}, | ||
199 | "omitempty": {""}, | ||
200 | "E": {""}, // E is included because the pointer is not empty, even though the string being pointed to is | ||
201 | } | ||
202 | if !reflect.DeepEqual(want, v) { | ||
203 | t.Errorf("Values(%v) returned %v, want %v", s, v, want) | ||
204 | } | ||
205 | } | ||
206 | |||
207 | type A struct { | ||
208 | B | ||
209 | } | ||
210 | |||
211 | type B struct { | ||
212 | C string | ||
213 | } | ||
214 | |||
215 | type D struct { | ||
216 | B | ||
217 | C string | ||
218 | } | ||
219 | |||
220 | type e struct { | ||
221 | B | ||
222 | C string | ||
223 | } | ||
224 | |||
225 | type F struct { | ||
226 | e | ||
227 | } | ||
228 | |||
229 | func TestValues_embeddedStructs(t *testing.T) { | ||
230 | tests := []struct { | ||
231 | in interface{} | ||
232 | want url.Values | ||
233 | }{ | ||
234 | { | ||
235 | A{B{C: "foo"}}, | ||
236 | url.Values{"C": {"foo"}}, | ||
237 | }, | ||
238 | { | ||
239 | D{B: B{C: "bar"}, C: "foo"}, | ||
240 | url.Values{"C": {"foo", "bar"}}, | ||
241 | }, | ||
242 | { | ||
243 | F{e{B: B{C: "bar"}, C: "foo"}}, // With unexported embed | ||
244 | url.Values{"C": {"foo", "bar"}}, | ||
245 | }, | ||
246 | } | ||
247 | |||
248 | for i, tt := range tests { | ||
249 | v, err := Values(tt.in) | ||
250 | if err != nil { | ||
251 | t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err) | ||
252 | } | ||
253 | |||
254 | if !reflect.DeepEqual(tt.want, v) { | ||
255 | t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want) | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | |||
260 | func TestValues_invalidInput(t *testing.T) { | ||
261 | _, err := Values("") | ||
262 | if err == nil { | ||
263 | t.Errorf("expected Values() to return an error on invalid input") | ||
264 | } | ||
265 | } | ||
266 | |||
267 | type EncodedArgs []string | ||
268 | |||
269 | func (m EncodedArgs) EncodeValues(key string, v *url.Values) error { | ||
270 | for i, arg := range m { | ||
271 | v.Set(fmt.Sprintf("%s.%d", key, i), arg) | ||
272 | } | ||
273 | return nil | ||
274 | } | ||
275 | |||
276 | func TestValues_Marshaler(t *testing.T) { | ||
277 | s := struct { | ||
278 | Args EncodedArgs `url:"arg"` | ||
279 | }{[]string{"a", "b", "c"}} | ||
280 | v, err := Values(s) | ||
281 | if err != nil { | ||
282 | t.Errorf("Values(%q) returned error: %v", s, err) | ||
283 | } | ||
284 | |||
285 | want := url.Values{ | ||
286 | "arg.0": {"a"}, | ||
287 | "arg.1": {"b"}, | ||
288 | "arg.2": {"c"}, | ||
289 | } | ||
290 | if !reflect.DeepEqual(want, v) { | ||
291 | t.Errorf("Values(%q) returned %v, want %v", s, v, want) | ||
292 | } | ||
293 | } | ||
294 | |||
295 | func TestValues_MarshalerWithNilPointer(t *testing.T) { | ||
296 | s := struct { | ||
297 | Args *EncodedArgs `url:"arg"` | ||
298 | }{} | ||
299 | v, err := Values(s) | ||
300 | if err != nil { | ||
301 | t.Errorf("Values(%v) returned error: %v", s, err) | ||
302 | } | ||
303 | |||
304 | want := url.Values{} | ||
305 | if !reflect.DeepEqual(want, v) { | ||
306 | t.Errorf("Values(%v) returned %v, want %v", s, v, want) | ||
307 | } | ||
308 | } | ||
309 | |||
310 | func TestTagParsing(t *testing.T) { | ||
311 | name, opts := parseTag("field,foobar,foo") | ||
312 | if name != "field" { | ||
313 | t.Fatalf("name = %q, want field", name) | ||
314 | } | ||
315 | for _, tt := range []struct { | ||
316 | opt string | ||
317 | want bool | ||
318 | }{ | ||
319 | {"foobar", true}, | ||
320 | {"foo", true}, | ||
321 | {"bar", false}, | ||
322 | {"field", false}, | ||
323 | } { | ||
324 | if opts.Contains(tt.opt) != tt.want { | ||
325 | t.Errorf("Contains(%q) = %v", tt.opt, !tt.want) | ||
326 | } | ||
327 | } | ||
328 | } | ||