|
|
@@ -0,0 +1,76 @@
|
|
|
+package router
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+/* routeElement contains a pre-compiled route pattern */
|
|
|
+type routeElement struct {
|
|
|
+ originalPath string
|
|
|
+ parsedPath string
|
|
|
+ parsedArgs int
|
|
|
+}
|
|
|
+
|
|
|
+/* Router object is safe for concurrent use after construction */
|
|
|
+type Router struct {
|
|
|
+ urlMap map[string]routeElement
|
|
|
+}
|
|
|
+
|
|
|
+/* addRoute adds or sets routing element in urlMap */
|
|
|
+func (r *Router) addRoute(key string, path string) error {
|
|
|
+ if _, ok := r.urlMap[key]; ok {
|
|
|
+ return ErrKeyExists
|
|
|
+ }
|
|
|
+ /* pre-compile path and replace path values with %v */
|
|
|
+ pathBuffer := strings.Builder{}
|
|
|
+ pathBuffer.Grow(len(path))
|
|
|
+ placeholderCount := 0
|
|
|
+ for idx := 0; idx < len(path); {
|
|
|
+ /* look for open bracket */
|
|
|
+ if path[idx] == '{' {
|
|
|
+ /* look for corresponding closing bracket */
|
|
|
+ closing := strings.IndexByte(path[idx:], '}')
|
|
|
+ if closing == -1 {
|
|
|
+ /* broken pattern, fail */
|
|
|
+ return ErrBrokenPattern
|
|
|
+ }
|
|
|
+ pathBuffer.WriteString("%v")
|
|
|
+ placeholderCount++
|
|
|
+ idx += closing + 1
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ pathBuffer.WriteByte(path[idx])
|
|
|
+ idx++
|
|
|
+ }
|
|
|
+ /* save pre-compiled path */
|
|
|
+ r.urlMap[key] = routeElement{
|
|
|
+ originalPath: path,
|
|
|
+ parsedPath: pathBuffer.String(),
|
|
|
+ parsedArgs: placeholderCount,
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/* Route returns path route by key */
|
|
|
+func (r *Router) Route(key string, args ...any) (string, error) {
|
|
|
+ /* lookup path in the database */
|
|
|
+ element, ok := r.urlMap[key]
|
|
|
+ if !ok {
|
|
|
+ return "", ErrMissingKey
|
|
|
+ }
|
|
|
+ /* return path unchanged if there are no arguments */
|
|
|
+ if len(args) != element.parsedArgs {
|
|
|
+ return "", ErrArgMismatch
|
|
|
+ }
|
|
|
+ return fmt.Sprintf(element.parsedPath, args...), nil
|
|
|
+}
|
|
|
+
|
|
|
+/* Pattern returns original path */
|
|
|
+func (r *Router) Pattern(key string) (string, error) {
|
|
|
+ if element, ok := r.urlMap[key]; ok {
|
|
|
+ return element.originalPath, nil
|
|
|
+ }
|
|
|
+ return "", ErrMissingKey
|
|
|
+}
|