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 }