Files
gopeed/pkg/api/routes.go
T

95 lines
2.1 KiB
Go

package api
import (
"fmt"
pathpkg "path"
"strings"
)
type HandlerFunc func(*Context) *Response
type RouteSpec struct {
Method string
Pattern string
Handler HandlerFunc
parts []routePart
}
type routePart struct {
literal string
param string
}
func newRoute(method, pattern string, handler HandlerFunc) RouteSpec {
pattern = cleanPath(pattern)
return RouteSpec{
Method: strings.ToUpper(method),
Pattern: pattern,
Handler: handler,
parts: parseRouteParts(pattern),
}
}
func validateRoutes(routes []RouteSpec) error {
patterns := make(map[string]struct{}, len(routes))
for _, route := range routes {
if route.Method == "" || route.Pattern == "" || route.Handler == nil {
return fmt.Errorf("invalid API route: %+v", route)
}
key := route.Method + " " + route.Pattern
if _, exists := patterns[key]; exists {
return fmt.Errorf("duplicate API route: %s", key)
}
patterns[key] = struct{}{}
}
return nil
}
func parseRouteParts(pattern string) []routePart {
pattern = strings.Trim(pattern, "/")
if pattern == "" {
return nil
}
segments := strings.Split(pattern, "/")
parts := make([]routePart, 0, len(segments))
for _, segment := range segments {
if strings.HasPrefix(segment, "{") && strings.HasSuffix(segment, "}") {
name := strings.TrimSuffix(strings.TrimPrefix(segment, "{"), "}")
parts = append(parts, routePart{param: name})
continue
}
parts = append(parts, routePart{literal: segment})
}
return parts
}
func (r RouteSpec) Match(rawPath string) (map[string]string, bool) {
path := strings.Trim(cleanPath(rawPath), "/")
if path == "" && len(r.parts) == 0 {
return map[string]string{}, true
}
segments := strings.Split(path, "/")
if len(segments) != len(r.parts) {
return nil, false
}
params := make(map[string]string)
for index, part := range r.parts {
if part.param != "" {
params[part.param] = segments[index]
continue
}
if part.literal != segments[index] {
return nil, false
}
}
return params, true
}
func cleanPath(rawPath string) string {
clean := pathpkg.Clean("/" + strings.TrimSpace(rawPath))
if clean == "." {
return "/"
}
return clean
}