77 lines
1.8 KiB
Go
77 lines
1.8 KiB
Go
package mux
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/a-h/gemini"
|
|
)
|
|
|
|
// Mux routes Gemini requests to the appropriate handler.
|
|
type Mux struct {
|
|
RouteHandlers []*RouteHandler
|
|
NotFoundHandler gemini.Handler
|
|
}
|
|
|
|
// NewMux creates a new Mux for routing requests.
|
|
func NewMux() *Mux {
|
|
return &Mux{
|
|
RouteHandlers: make([]*RouteHandler, 0),
|
|
NotFoundHandler: gemini.NotFoundHandler(),
|
|
}
|
|
}
|
|
|
|
// AddRoute to the mux.
|
|
func (m *Mux) AddRoute(pattern string, handler gemini.Handler) {
|
|
rh := &RouteHandler{
|
|
Route: NewRoute(pattern),
|
|
Handler: handler,
|
|
}
|
|
m.RouteHandlers = append(m.RouteHandlers, rh)
|
|
}
|
|
|
|
// RouteHandler is the Handler to use for a given Route.
|
|
type RouteHandler struct {
|
|
Route *Route
|
|
Handler gemini.Handler
|
|
}
|
|
|
|
// contextKey used to store the route handler in the request context.
|
|
type contextKey string
|
|
|
|
// matchedRouteContextKey is the key stored in the context.
|
|
const matchedRouteContextKey = contextKey("matchedRoute")
|
|
|
|
// MatchedRoute is provided in the context to Gemini handlers that use the router.
|
|
type MatchedRoute struct {
|
|
Pattern string
|
|
PathVars map[string]string
|
|
}
|
|
|
|
func (m *Mux) ServeGemini(w gemini.ResponseWriter, r *gemini.Request) {
|
|
s := r.URL.Path
|
|
s = strings.TrimSuffix(s, "/")
|
|
s = strings.TrimPrefix(s, "/")
|
|
segments := strings.Split(s, "/")
|
|
|
|
for _, rh := range m.RouteHandlers {
|
|
v, ok := rh.Route.Match(segments)
|
|
if ok {
|
|
mr := MatchedRoute{
|
|
Pattern: rh.Route.Pattern,
|
|
PathVars: v,
|
|
}
|
|
r.Context = context.WithValue(r.Context, matchedRouteContextKey, mr)
|
|
rh.Handler.ServeGemini(w, r)
|
|
return
|
|
}
|
|
}
|
|
m.NotFoundHandler.ServeGemini(w, r)
|
|
}
|
|
|
|
// GetMatchedRoute returns the route that was matched by the router, along with any path variables extracted from the URL.
|
|
func GetMatchedRoute(ctx context.Context) (mr MatchedRoute, ok bool) {
|
|
mr, ok = ctx.Value(matchedRouteContextKey).(MatchedRoute)
|
|
return mr, ok
|
|
}
|