]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/google.golang.org/api/googleapi/internal/uritemplates/uritemplates.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / google.golang.org / api / googleapi / internal / uritemplates / uritemplates.go
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 }