diff options
Diffstat (limited to 'vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go')
-rw-r--r-- | vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go b/vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go new file mode 100644 index 0000000..63bf053 --- /dev/null +++ b/vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go | |||
@@ -0,0 +1,248 @@ | |||
1 | // Copyright 2013 Joshua Tacoma. 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 uritemplates is a level 3 implementation of RFC 6570 (URI | ||
6 | // Template, http://tools.ietf.org/html/rfc6570). | ||
7 | // uritemplates does not support composite values (in Go: slices or maps) | ||
8 | // and so does not qualify as a level 4 implementation. | ||
9 | package uritemplates | ||
10 | |||
11 | import ( | ||
12 | "bytes" | ||
13 | "errors" | ||
14 | "regexp" | ||
15 | "strconv" | ||
16 | "strings" | ||
17 | ) | ||
18 | |||
19 | var ( | ||
20 | unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]") | ||
21 | reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]") | ||
22 | validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$") | ||
23 | hex = []byte("0123456789ABCDEF") | ||
24 | ) | ||
25 | |||
26 | func pctEncode(src []byte) []byte { | ||
27 | dst := make([]byte, len(src)*3) | ||
28 | for i, b := range src { | ||
29 | buf := dst[i*3 : i*3+3] | ||
30 | buf[0] = 0x25 | ||
31 | buf[1] = hex[b/16] | ||
32 | buf[2] = hex[b%16] | ||
33 | } | ||
34 | return dst | ||
35 | } | ||
36 | |||
37 | // pairWriter is a convenience struct which allows escaped and unescaped | ||
38 | // versions of the template to be written in parallel. | ||
39 | type pairWriter struct { | ||
40 | escaped, unescaped bytes.Buffer | ||
41 | } | ||
42 | |||
43 | // Write writes the provided string directly without any escaping. | ||
44 | func (w *pairWriter) Write(s string) { | ||
45 | w.escaped.WriteString(s) | ||
46 | w.unescaped.WriteString(s) | ||
47 | } | ||
48 | |||
49 | // Escape writes the provided string, escaping the string for the | ||
50 | // escaped output. | ||
51 | func (w *pairWriter) Escape(s string, allowReserved bool) { | ||
52 | w.unescaped.WriteString(s) | ||
53 | if allowReserved { | ||
54 | w.escaped.Write(reserved.ReplaceAllFunc([]byte(s), pctEncode)) | ||
55 | } else { | ||
56 | w.escaped.Write(unreserved.ReplaceAllFunc([]byte(s), pctEncode)) | ||
57 | } | ||
58 | } | ||
59 | |||
60 | // Escaped returns the escaped string. | ||
61 | func (w *pairWriter) Escaped() string { | ||
62 | return w.escaped.String() | ||
63 | } | ||
64 | |||
65 | // Unescaped returns the unescaped string. | ||
66 | func (w *pairWriter) Unescaped() string { | ||
67 | return w.unescaped.String() | ||
68 | } | ||
69 | |||
70 | // A uriTemplate is a parsed representation of a URI template. | ||
71 | type uriTemplate struct { | ||
72 | raw string | ||
73 | parts []templatePart | ||
74 | } | ||
75 | |||
76 | // parse parses a URI template string into a uriTemplate object. | ||
77 | func parse(rawTemplate string) (*uriTemplate, error) { | ||
78 | split := strings.Split(rawTemplate, "{") | ||
79 | parts := make([]templatePart, len(split)*2-1) | ||
80 | for i, s := range split { | ||
81 | if i == 0 { | ||
82 | if strings.Contains(s, "}") { | ||
83 | return nil, errors.New("unexpected }") | ||
84 | } | ||
85 | parts[i].raw = s | ||
86 | continue | ||
87 | } | ||
88 | subsplit := strings.Split(s, "}") | ||
89 | if len(subsplit) != 2 { | ||
90 | return nil, errors.New("malformed template") | ||
91 | } | ||
92 | expression := subsplit[0] | ||
93 | var err error | ||
94 | parts[i*2-1], err = parseExpression(expression) | ||
95 | if err != nil { | ||
96 | return nil, err | ||
97 | } | ||
98 | parts[i*2].raw = subsplit[1] | ||
99 | } | ||
100 | return &uriTemplate{ | ||
101 | raw: rawTemplate, | ||
102 | parts: parts, | ||
103 | }, nil | ||
104 | } | ||
105 | |||
106 | type templatePart struct { | ||
107 | raw string | ||
108 | terms []templateTerm | ||
109 | first string | ||
110 | sep string | ||
111 | named bool | ||
112 | ifemp string | ||
113 | allowReserved bool | ||
114 | } | ||
115 | |||
116 | type templateTerm struct { | ||
117 | name string | ||
118 | explode bool | ||
119 | truncate int | ||
120 | } | ||
121 | |||
122 | func parseExpression(expression string) (result templatePart, err error) { | ||
123 | switch expression[0] { | ||
124 | case '+': | ||
125 | result.sep = "," | ||
126 | result.allowReserved = true | ||
127 | expression = expression[1:] | ||
128 | case '.': | ||
129 | result.first = "." | ||
130 | result.sep = "." | ||
131 | expression = expression[1:] | ||
132 | case '/': | ||
133 | result.first = "/" | ||
134 | result.sep = "/" | ||
135 | expression = expression[1:] | ||
136 | case ';': | ||
137 | result.first = ";" | ||
138 | result.sep = ";" | ||
139 | result.named = true | ||
140 | expression = expression[1:] | ||
141 | case '?': | ||
142 | result.first = "?" | ||
143 | result.sep = "&" | ||
144 | result.named = true | ||
145 | result.ifemp = "=" | ||
146 | expression = expression[1:] | ||
147 | case '&': | ||
148 | result.first = "&" | ||
149 | result.sep = "&" | ||
150 | result.named = true | ||
151 | result.ifemp = "=" | ||
152 | expression = expression[1:] | ||
153 | case '#': | ||
154 | result.first = "#" | ||
155 | result.sep = "," | ||
156 | result.allowReserved = true | ||
157 | expression = expression[1:] | ||
158 | default: | ||
159 | result.sep = "," | ||
160 | } | ||
161 | rawterms := strings.Split(expression, ",") | ||
162 | result.terms = make([]templateTerm, len(rawterms)) | ||
163 | for i, raw := range rawterms { | ||
164 | result.terms[i], err = parseTerm(raw) | ||
165 | if err != nil { | ||
166 | break | ||
167 | } | ||
168 | } | ||
169 | return result, err | ||
170 | } | ||
171 | |||
172 | func parseTerm(term string) (result templateTerm, err error) { | ||
173 | // TODO(djd): Remove "*" suffix parsing once we check that no APIs have | ||
174 | // mistakenly used that attribute. | ||
175 | if strings.HasSuffix(term, "*") { | ||
176 | result.explode = true | ||
177 | term = term[:len(term)-1] | ||
178 | } | ||
179 | split := strings.Split(term, ":") | ||
180 | if len(split) == 1 { | ||
181 | result.name = term | ||
182 | } else if len(split) == 2 { | ||
183 | result.name = split[0] | ||
184 | var parsed int64 | ||
185 | parsed, err = strconv.ParseInt(split[1], 10, 0) | ||
186 | result.truncate = int(parsed) | ||
187 | } else { | ||
188 | err = errors.New("multiple colons in same term") | ||
189 | } | ||
190 | if !validname.MatchString(result.name) { | ||
191 | err = errors.New("not a valid name: " + result.name) | ||
192 | } | ||
193 | if result.explode && result.truncate > 0 { | ||
194 | err = errors.New("both explode and prefix modifers on same term") | ||
195 | } | ||
196 | return result, err | ||
197 | } | ||
198 | |||
199 | // Expand expands a URI template with a set of values to produce the | ||
200 | // resultant URI. Two forms of the result are returned: one with all the | ||
201 | // elements escaped, and one with the elements unescaped. | ||
202 | func (t *uriTemplate) Expand(values map[string]string) (escaped, unescaped string) { | ||
203 | var w pairWriter | ||
204 | for _, p := range t.parts { | ||
205 | p.expand(&w, values) | ||
206 | } | ||
207 | return w.Escaped(), w.Unescaped() | ||
208 | } | ||
209 | |||
210 | func (tp *templatePart) expand(w *pairWriter, values map[string]string) { | ||
211 | if len(tp.raw) > 0 { | ||
212 | w.Write(tp.raw) | ||
213 | return | ||
214 | } | ||
215 | var first = true | ||
216 | for _, term := range tp.terms { | ||
217 | value, exists := values[term.name] | ||
218 | if !exists { | ||
219 | continue | ||
220 | } | ||
221 | if first { | ||
222 | w.Write(tp.first) | ||
223 | first = false | ||
224 | } else { | ||
225 | w.Write(tp.sep) | ||
226 | } | ||
227 | tp.expandString(w, term, value) | ||
228 | } | ||
229 | } | ||
230 | |||
231 | func (tp *templatePart) expandName(w *pairWriter, name string, empty bool) { | ||
232 | if tp.named { | ||
233 | w.Write(name) | ||
234 | if empty { | ||
235 | w.Write(tp.ifemp) | ||
236 | } else { | ||
237 | w.Write("=") | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | func (tp *templatePart) expandString(w *pairWriter, t templateTerm, s string) { | ||
243 | if len(s) > t.truncate && t.truncate > 0 { | ||
244 | s = s[:t.truncate] | ||
245 | } | ||
246 | tp.expandName(w, t.name, len(s) == 0) | ||
247 | w.Escape(s, tp.allowReserved) | ||
248 | } | ||