From ac024567e880f0da59557f0051018f4ac932c6ad Mon Sep 17 00:00:00 2001 From: tjpcc Date: Thu, 2 Feb 2023 16:15:53 -0700 Subject: Initial Router work. - Router type, supports: adding handlers, serving, fetching the matching handler for a route. - Private PathTree type handles the modified radix trie. --- router.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 router.go (limited to 'router.go') diff --git a/router.go b/router.go new file mode 100644 index 0000000..8408246 --- /dev/null +++ b/router.go @@ -0,0 +1,71 @@ +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{} -- cgit v1.2.3