Capstan is a Golang web framework that shares some similarities with others in its segment.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
2.8KB

  1. package capstan
  2. import (
  3. "strings"
  4. )
  5. // ParseRoute processes the specified route, processes it, and returns a
  6. // routeProps struct describing the route using go-chi syntax.
  7. //
  8. // Route parsing is somewhat analogous to go-chi but uses a different syntax. In
  9. // particular, route variables are delineated by "<" and ">" rather than "{" and
  10. // "}" as per go-chi. Further, besides regex types, Capstan routes also define
  11. // string, float, float32, float64, int, int32, and int64 types. Route syntax
  12. // is:
  13. //
  14. // String:
  15. //
  16. // /<varname:string>
  17. //
  18. // Integer types:
  19. //
  20. // /<varname:int32>
  21. //
  22. // Regular expressions:
  23. //
  24. // /<varname:regex>
  25. func (r *Router) ParseRoute(route string) *routeProps {
  26. props := &routeProps{
  27. params: make(map[string]string),
  28. }
  29. if route == "" {
  30. route = "/"
  31. }
  32. if route == "/" {
  33. props.route = "/"
  34. props.baseRoute = "/"
  35. props.slashIsDefault = true
  36. props.optionalSlash = true
  37. return props
  38. }
  39. if route[len(route)-1:] == "/" {
  40. props.mandatorySlash = true
  41. } else if strings.HasSuffix(route, "?") {
  42. props.optionalSlash = true
  43. if len(route) >= 2 && route[len(route)-2:] == "/?" {
  44. props.slashIsDefault = true
  45. }
  46. route = route[:len(route)-1]
  47. } else if strings.HasSuffix(route, "!") {
  48. props.mandatoryNoSlash = true
  49. if len(route) >= 2 && route[len(route)-2:] != "/!" {
  50. route = route[:len(route)-1] + "/"
  51. } else {
  52. route = route[:len(route)-1]
  53. }
  54. } else if strings.HasSuffix(route, "+") {
  55. props.mandatorySlash = true
  56. // Accept routes with just a + suffix, e.g. "/route+".
  57. if len(route) >= 2 && route[len(route)-2:] != "/+" {
  58. route = route[:len(route)-1] + "/"
  59. } else {
  60. route = route[:len(route)-1]
  61. }
  62. } else {
  63. props.optionalSlash = true
  64. }
  65. open := 0
  66. param := ""
  67. paramType := ""
  68. colon := false
  69. base := true
  70. for _, c := range route {
  71. if open == 0 && c == '/' {
  72. props.route += string(c)
  73. if base {
  74. props.baseRoute += string(c)
  75. }
  76. continue
  77. }
  78. if c == '<' {
  79. base = false
  80. open++
  81. if open == 1 {
  82. props.route += "{"
  83. continue
  84. }
  85. } else if c == '>' {
  86. open--
  87. if open == 0 {
  88. props.params[param] = paramType
  89. switch paramType {
  90. case "string":
  91. case "float":
  92. case "float32":
  93. case "float64":
  94. case "int":
  95. case "int32":
  96. case "int64":
  97. case "integer":
  98. default:
  99. // Most likely a regex type.
  100. props.route += ":" + paramType
  101. }
  102. props.route += "}"
  103. colon = false
  104. param = ""
  105. paramType = ""
  106. continue
  107. }
  108. } else {
  109. if open == 0 && c != '?' {
  110. props.route += string(c)
  111. }
  112. }
  113. if open == 1 && c == ':' {
  114. colon = true
  115. continue
  116. }
  117. if open == 1 {
  118. if !colon {
  119. param += string(c)
  120. props.route += string(c)
  121. } else {
  122. paramType += string(c)
  123. }
  124. }
  125. if open > 1 {
  126. paramType += string(c)
  127. }
  128. if base {
  129. props.baseRoute += string(c)
  130. }
  131. }
  132. if open >= 1 {
  133. // TODO: Handle error.
  134. }
  135. return props
  136. }