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 }