expression.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright (C) 2016 Kohei YOSHIDA. All rights reserved.
  2. //
  3. // This program is free software; you can redistribute it and/or
  4. // modify it under the terms of The BSD 3-Clause License
  5. // that can be found in the LICENSE file.
  6. package uritemplate
  7. import (
  8. "regexp"
  9. "strconv"
  10. "strings"
  11. )
  12. type template interface {
  13. expand(*strings.Builder, Values) error
  14. regexp(*strings.Builder)
  15. }
  16. type literals string
  17. func (l literals) expand(b *strings.Builder, _ Values) error {
  18. b.WriteString(string(l))
  19. return nil
  20. }
  21. func (l literals) regexp(b *strings.Builder) {
  22. b.WriteString("(?:")
  23. b.WriteString(regexp.QuoteMeta(string(l)))
  24. b.WriteByte(')')
  25. }
  26. type varspec struct {
  27. name string
  28. maxlen int
  29. explode bool
  30. }
  31. type expression struct {
  32. vars []varspec
  33. op parseOp
  34. first string
  35. sep string
  36. named bool
  37. ifemp string
  38. escape escapeFunc
  39. allow runeClass
  40. }
  41. func (e *expression) init() {
  42. switch e.op {
  43. case parseOpSimple:
  44. e.sep = ","
  45. e.escape = escapeExceptU
  46. e.allow = runeClassU
  47. case parseOpPlus:
  48. e.sep = ","
  49. e.escape = escapeExceptUR
  50. e.allow = runeClassUR
  51. case parseOpCrosshatch:
  52. e.first = "#"
  53. e.sep = ","
  54. e.escape = escapeExceptUR
  55. e.allow = runeClassUR
  56. case parseOpDot:
  57. e.first = "."
  58. e.sep = "."
  59. e.escape = escapeExceptU
  60. e.allow = runeClassU
  61. case parseOpSlash:
  62. e.first = "/"
  63. e.sep = "/"
  64. e.escape = escapeExceptU
  65. e.allow = runeClassU
  66. case parseOpSemicolon:
  67. e.first = ";"
  68. e.sep = ";"
  69. e.named = true
  70. e.escape = escapeExceptU
  71. e.allow = runeClassU
  72. case parseOpQuestion:
  73. e.first = "?"
  74. e.sep = "&"
  75. e.named = true
  76. e.ifemp = "="
  77. e.escape = escapeExceptU
  78. e.allow = runeClassU
  79. case parseOpAmpersand:
  80. e.first = "&"
  81. e.sep = "&"
  82. e.named = true
  83. e.ifemp = "="
  84. e.escape = escapeExceptU
  85. e.allow = runeClassU
  86. }
  87. }
  88. func (e *expression) expand(w *strings.Builder, values Values) error {
  89. first := true
  90. for _, varspec := range e.vars {
  91. value := values.Get(varspec.name)
  92. if !value.Valid() {
  93. continue
  94. }
  95. if first {
  96. w.WriteString(e.first)
  97. first = false
  98. } else {
  99. w.WriteString(e.sep)
  100. }
  101. if err := value.expand(w, varspec, e); err != nil {
  102. return err
  103. }
  104. }
  105. return nil
  106. }
  107. func (e *expression) regexp(b *strings.Builder) {
  108. if e.first != "" {
  109. b.WriteString("(?:") // $1
  110. b.WriteString(regexp.QuoteMeta(e.first))
  111. }
  112. b.WriteByte('(') // $2
  113. runeClassToRegexp(b, e.allow, e.named || e.vars[0].explode)
  114. if len(e.vars) > 1 || e.vars[0].explode {
  115. max := len(e.vars) - 1
  116. for i := 0; i < len(e.vars); i++ {
  117. if e.vars[i].explode {
  118. max = -1
  119. break
  120. }
  121. }
  122. b.WriteString("(?:") // $3
  123. b.WriteString(regexp.QuoteMeta(e.sep))
  124. runeClassToRegexp(b, e.allow, e.named || max < 0)
  125. b.WriteByte(')') // $3
  126. if max > 0 {
  127. b.WriteString("{0,")
  128. b.WriteString(strconv.Itoa(max))
  129. b.WriteByte('}')
  130. } else {
  131. b.WriteByte('*')
  132. }
  133. }
  134. b.WriteByte(')') // $2
  135. if e.first != "" {
  136. b.WriteByte(')') // $1
  137. }
  138. b.WriteByte('?')
  139. }
  140. func runeClassToRegexp(b *strings.Builder, class runeClass, named bool) {
  141. b.WriteString("(?:(?:[")
  142. if class&runeClassR == 0 {
  143. b.WriteString(`\x2c`)
  144. if named {
  145. b.WriteString(`\x3d`)
  146. }
  147. }
  148. if class&runeClassU == runeClassU {
  149. b.WriteString(reUnreserved)
  150. }
  151. if class&runeClassR == runeClassR {
  152. b.WriteString(reReserved)
  153. }
  154. b.WriteString("]")
  155. b.WriteString("|%[[:xdigit:]][[:xdigit:]]")
  156. b.WriteString(")*)")
  157. }