route.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. package router
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. /* routeElement contains a pre-compiled route pattern */
  7. type routeElement struct {
  8. originalPath string
  9. parsedPath string
  10. parsedArgs int
  11. }
  12. /* Router object is safe for concurrent use after construction */
  13. type Router struct {
  14. urlMap map[string]routeElement
  15. }
  16. /* addRoute adds or sets routing element in urlMap */
  17. func (r *Router) addRoute(key string, path string) error {
  18. if _, ok := r.urlMap[key]; ok {
  19. return ErrKeyExists
  20. }
  21. /* pre-compile path and replace path values with %v */
  22. pathBuffer := strings.Builder{}
  23. pathBuffer.Grow(len(path))
  24. placeholderCount := 0
  25. for idx := 0; idx < len(path); {
  26. /* look for open bracket */
  27. if path[idx] == '{' {
  28. /* look for corresponding closing bracket */
  29. closing := strings.IndexByte(path[idx:], '}')
  30. if closing == -1 {
  31. /* broken pattern, fail */
  32. return ErrBrokenPattern
  33. }
  34. pathBuffer.WriteString("%v")
  35. placeholderCount++
  36. idx += closing + 1
  37. continue
  38. }
  39. pathBuffer.WriteByte(path[idx])
  40. idx++
  41. }
  42. /* save pre-compiled path */
  43. r.urlMap[key] = routeElement{
  44. originalPath: path,
  45. parsedPath: pathBuffer.String(),
  46. parsedArgs: placeholderCount,
  47. }
  48. return nil
  49. }
  50. /* Route returns path route by key */
  51. func (r *Router) Route(key string, args ...any) (string, error) {
  52. /* lookup path in the database */
  53. element, ok := r.urlMap[key]
  54. if !ok {
  55. return "", ErrMissingKey
  56. }
  57. /* return path unchanged if there are no arguments */
  58. if len(args) != element.parsedArgs {
  59. return "", ErrArgMismatch
  60. }
  61. return fmt.Sprintf(element.parsedPath, args...), nil
  62. }
  63. /* Pattern returns original path */
  64. func (r *Router) Pattern(key string) (string, error) {
  65. if element, ok := r.urlMap[key]; ok {
  66. return element.originalPath, nil
  67. }
  68. return "", ErrMissingKey
  69. }