package gus import ( "context" "tildegit.org/tjp/gus/internal" ) // Router stores a mapping of request path patterns to handlers. // // Pattern may begin with "/" and then contain slash-delimited segments. // - Segments beginning with colon (:) are wildcards and will match any path // segment at that location. It may optionally have a word after the colon, // which will be the parameter name the path segment is captured into. // - Segments beginning with asterisk (*) are remainder wildcards. This must // come last and will capture any remainder of the path. It may have a name // after the asterisk which will be the parameter name. // - Any other segment in the pattern must match a path segment exactly. // // These patterns do not match any path which shares a prefix, rather then // full path must match a pattern. If you want to only match a prefix of the // path you can end the pattern with a *remainder segment. // // The zero value is a usable Router which will fail to match any requst path. type Router struct { tree internal.PathTree[Handler] } // Route adds a handler to the router under a path pattern. func (r Router) Route(pattern string, handler Handler) { r.tree.Add(pattern, handler) } // Handler matches against the request path and dipatches to a route handler. // // If no route matches, it returns a nil response. // Captured path parameters will be stored in the context passed into the handler // and can be retrieved with RouteParams(). func (r Router) Handler(ctx context.Context, request *Request) *Response { handler, params := r.Match(request) if handler == nil { return nil } return handler(context.WithValue(ctx, routeParamsKey, params), request) } // Match returns the matched handler and captured path parameters, or nils. func (r Router) Match(request *Request) (Handler, map[string]string) { handler, params := r.tree.Match(request.Path) if handler == nil { return nil, nil } return *handler, params } // RouteParams gathers captured path parameters from the request context. // // If the context doesn't contain a parameter map, it returns nil. // If Router was used but no parameters were captured in the pattern, it // returns a non-nil empty map. func RouteParams(ctx context.Context) map[string]string { if m, ok := ctx.Value(routeParamsKey).(map[string]string); ok { return m } return nil } type routeParamsKeyType struct{} var routeParamsKey = routeParamsKeyType{}