uritemplates.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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 4 implementation of RFC 6570 (URI
  5. // Template, http://tools.ietf.org/html/rfc6570).
  6. //
  7. // To use uritemplates, parse a template string and expand it with a value
  8. // map:
  9. //
  10. // template, _ := uritemplates.Parse("https://api.github.com/repos{/user,repo}")
  11. // values := make(map[string]interface{})
  12. // values["user"] = "jtacoma"
  13. // values["repo"] = "uritemplates"
  14. // expanded, _ := template.ExpandString(values)
  15. // fmt.Printf(expanded)
  16. //
  17. package uritemplates
  18. import (
  19. "bytes"
  20. "errors"
  21. "fmt"
  22. "reflect"
  23. "regexp"
  24. "strconv"
  25. "strings"
  26. )
  27. var (
  28. unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
  29. reserved = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
  30. validname = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
  31. hex = []byte("0123456789ABCDEF")
  32. )
  33. func pctEncode(src []byte) []byte {
  34. dst := make([]byte, len(src)*3)
  35. for i, b := range src {
  36. buf := dst[i*3 : i*3+3]
  37. buf[0] = 0x25
  38. buf[1] = hex[b/16]
  39. buf[2] = hex[b%16]
  40. }
  41. return dst
  42. }
  43. func escape(s string, allowReserved bool) (escaped string) {
  44. if allowReserved {
  45. escaped = string(reserved.ReplaceAllFunc([]byte(s), pctEncode))
  46. } else {
  47. escaped = string(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
  48. }
  49. return escaped
  50. }
  51. // A UriTemplate is a parsed representation of a URI template.
  52. type UriTemplate struct {
  53. raw string
  54. parts []templatePart
  55. }
  56. // Parse parses a URI template string into a UriTemplate object.
  57. func Parse(rawtemplate string) (template *UriTemplate, err error) {
  58. template = new(UriTemplate)
  59. template.raw = rawtemplate
  60. split := strings.Split(rawtemplate, "{")
  61. template.parts = make([]templatePart, len(split)*2-1)
  62. for i, s := range split {
  63. if i == 0 {
  64. if strings.Contains(s, "}") {
  65. err = errors.New("unexpected }")
  66. break
  67. }
  68. template.parts[i].raw = s
  69. } else {
  70. subsplit := strings.Split(s, "}")
  71. if len(subsplit) != 2 {
  72. err = errors.New("malformed template")
  73. break
  74. }
  75. expression := subsplit[0]
  76. template.parts[i*2-1], err = parseExpression(expression)
  77. if err != nil {
  78. break
  79. }
  80. template.parts[i*2].raw = subsplit[1]
  81. }
  82. }
  83. if err != nil {
  84. template = nil
  85. }
  86. return template, err
  87. }
  88. type templatePart struct {
  89. raw string
  90. terms []templateTerm
  91. first string
  92. sep string
  93. named bool
  94. ifemp string
  95. allowReserved bool
  96. }
  97. type templateTerm struct {
  98. name string
  99. explode bool
  100. truncate int
  101. }
  102. func parseExpression(expression string) (result templatePart, err error) {
  103. switch expression[0] {
  104. case '+':
  105. result.sep = ","
  106. result.allowReserved = true
  107. expression = expression[1:]
  108. case '.':
  109. result.first = "."
  110. result.sep = "."
  111. expression = expression[1:]
  112. case '/':
  113. result.first = "/"
  114. result.sep = "/"
  115. expression = expression[1:]
  116. case ';':
  117. result.first = ";"
  118. result.sep = ";"
  119. result.named = true
  120. expression = expression[1:]
  121. case '?':
  122. result.first = "?"
  123. result.sep = "&"
  124. result.named = true
  125. result.ifemp = "="
  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.allowReserved = true
  137. expression = expression[1:]
  138. default:
  139. result.sep = ","
  140. }
  141. rawterms := strings.Split(expression, ",")
  142. result.terms = make([]templateTerm, len(rawterms))
  143. for i, raw := range rawterms {
  144. result.terms[i], err = parseTerm(raw)
  145. if err != nil {
  146. break
  147. }
  148. }
  149. return result, err
  150. }
  151. func parseTerm(term string) (result templateTerm, err error) {
  152. if strings.HasSuffix(term, "*") {
  153. result.explode = true
  154. term = term[:len(term)-1]
  155. }
  156. split := strings.Split(term, ":")
  157. if len(split) == 1 {
  158. result.name = term
  159. } else if len(split) == 2 {
  160. result.name = split[0]
  161. var parsed int64
  162. parsed, err = strconv.ParseInt(split[1], 10, 0)
  163. result.truncate = int(parsed)
  164. } else {
  165. err = errors.New("multiple colons in same term")
  166. }
  167. if !validname.MatchString(result.name) {
  168. err = errors.New("not a valid name: " + result.name)
  169. }
  170. if result.explode && result.truncate > 0 {
  171. err = errors.New("both explode and prefix modifers on same term")
  172. }
  173. return result, err
  174. }
  175. // Expand expands a URI template with a set of values to produce a string.
  176. func (self *UriTemplate) Expand(value interface{}) (string, error) {
  177. values, ismap := value.(map[string]interface{})
  178. if !ismap {
  179. if m, ismap := struct2map(value); !ismap {
  180. return "", errors.New("expected map[string]interface{}, struct, or pointer to struct.")
  181. } else {
  182. return self.Expand(m)
  183. }
  184. }
  185. var buf bytes.Buffer
  186. for _, p := range self.parts {
  187. err := p.expand(&buf, values)
  188. if err != nil {
  189. return "", err
  190. }
  191. }
  192. return buf.String(), nil
  193. }
  194. func (self *templatePart) expand(buf *bytes.Buffer, values map[string]interface{}) error {
  195. if len(self.raw) > 0 {
  196. buf.WriteString(self.raw)
  197. return nil
  198. }
  199. var zeroLen = buf.Len()
  200. buf.WriteString(self.first)
  201. var firstLen = buf.Len()
  202. for _, term := range self.terms {
  203. value, exists := values[term.name]
  204. if !exists {
  205. continue
  206. }
  207. if buf.Len() != firstLen {
  208. buf.WriteString(self.sep)
  209. }
  210. switch v := value.(type) {
  211. case string:
  212. self.expandString(buf, term, v)
  213. case []interface{}:
  214. self.expandArray(buf, term, v)
  215. case map[string]interface{}:
  216. if term.truncate > 0 {
  217. return errors.New("cannot truncate a map expansion")
  218. }
  219. self.expandMap(buf, term, v)
  220. default:
  221. if m, ismap := struct2map(value); ismap {
  222. if term.truncate > 0 {
  223. return errors.New("cannot truncate a map expansion")
  224. }
  225. self.expandMap(buf, term, m)
  226. } else {
  227. str := fmt.Sprintf("%v", value)
  228. self.expandString(buf, term, str)
  229. }
  230. }
  231. }
  232. if buf.Len() == firstLen {
  233. original := buf.Bytes()[:zeroLen]
  234. buf.Reset()
  235. buf.Write(original)
  236. }
  237. return nil
  238. }
  239. func (self *templatePart) expandName(buf *bytes.Buffer, name string, empty bool) {
  240. if self.named {
  241. buf.WriteString(name)
  242. if empty {
  243. buf.WriteString(self.ifemp)
  244. } else {
  245. buf.WriteString("=")
  246. }
  247. }
  248. }
  249. func (self *templatePart) expandString(buf *bytes.Buffer, t templateTerm, s string) {
  250. if len(s) > t.truncate && t.truncate > 0 {
  251. s = s[:t.truncate]
  252. }
  253. self.expandName(buf, t.name, len(s) == 0)
  254. buf.WriteString(escape(s, self.allowReserved))
  255. }
  256. func (self *templatePart) expandArray(buf *bytes.Buffer, t templateTerm, a []interface{}) {
  257. if len(a) == 0 {
  258. return
  259. } else if !t.explode {
  260. self.expandName(buf, t.name, false)
  261. }
  262. for i, value := range a {
  263. if t.explode && i > 0 {
  264. buf.WriteString(self.sep)
  265. } else if i > 0 {
  266. buf.WriteString(",")
  267. }
  268. var s string
  269. switch v := value.(type) {
  270. case string:
  271. s = v
  272. default:
  273. s = fmt.Sprintf("%v", v)
  274. }
  275. if len(s) > t.truncate && t.truncate > 0 {
  276. s = s[:t.truncate]
  277. }
  278. if self.named && t.explode {
  279. self.expandName(buf, t.name, len(s) == 0)
  280. }
  281. buf.WriteString(escape(s, self.allowReserved))
  282. }
  283. }
  284. func (self *templatePart) expandMap(buf *bytes.Buffer, t templateTerm, m map[string]interface{}) {
  285. if len(m) == 0 {
  286. return
  287. }
  288. if !t.explode {
  289. self.expandName(buf, t.name, len(m) == 0)
  290. }
  291. var firstLen = buf.Len()
  292. for k, value := range m {
  293. if firstLen != buf.Len() {
  294. if t.explode {
  295. buf.WriteString(self.sep)
  296. } else {
  297. buf.WriteString(",")
  298. }
  299. }
  300. var s string
  301. switch v := value.(type) {
  302. case string:
  303. s = v
  304. default:
  305. s = fmt.Sprintf("%v", v)
  306. }
  307. if t.explode {
  308. buf.WriteString(escape(k, self.allowReserved))
  309. buf.WriteRune('=')
  310. buf.WriteString(escape(s, self.allowReserved))
  311. } else {
  312. buf.WriteString(escape(k, self.allowReserved))
  313. buf.WriteRune(',')
  314. buf.WriteString(escape(s, self.allowReserved))
  315. }
  316. }
  317. }
  318. func struct2map(v interface{}) (map[string]interface{}, bool) {
  319. value := reflect.ValueOf(v)
  320. switch value.Type().Kind() {
  321. case reflect.Ptr:
  322. return struct2map(value.Elem().Interface())
  323. case reflect.Struct:
  324. m := make(map[string]interface{})
  325. for i := 0; i < value.NumField(); i++ {
  326. tag := value.Type().Field(i).Tag
  327. var name string
  328. if strings.Contains(string(tag), ":") {
  329. name = tag.Get("uri")
  330. } else {
  331. name = strings.TrimSpace(string(tag))
  332. }
  333. if len(name) == 0 {
  334. name = value.Type().Field(i).Name
  335. }
  336. m[name] = value.Field(i).Interface()
  337. }
  338. return m, true
  339. }
  340. return nil, false
  341. }