Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 

249 wiersze
5.9 KiB

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