Search Apps Documentation Source Content File Folder Download Copy Actions Download

router.gno

4.17 Kb · 171 lines
  1package mux
  2
  3import (
  4	"net/url"
  5	"strings"
  6)
  7
  8// Router handles the routing and rendering logic.
  9type Router struct {
 10	routes          []Handler
 11	NotFoundHandler NotFoundHandler
 12}
 13
 14// NewRouter creates a new Router instance.
 15func NewRouter() *Router {
 16	return &Router{
 17		routes:          make([]Handler, 0),
 18		NotFoundHandler: defaultNotFoundHandler,
 19	}
 20}
 21
 22// Render renders the output for the given path using the registered route handler.
 23func (r *Router) Render(reqPath string) string {
 24	clearPath, rawQuery, _ := strings.Cut(reqPath, "?")
 25	query, _ := url.ParseQuery(rawQuery)
 26	reqParts := strings.Split(clearPath, "/")
 27
 28	for _, route := range r.routes {
 29		patParts := strings.Split(route.Pattern, "/")
 30		wildcard := false
 31		for _, part := range patParts {
 32			if part == "*" {
 33				wildcard = true
 34				break
 35			}
 36		}
 37		if !wildcard && len(patParts) != len(reqParts) {
 38			continue
 39		}
 40
 41		match := true
 42		for i := 0; i < len(patParts); i++ {
 43			patPart := patParts[i]
 44			reqPart := reqParts[i]
 45
 46			if patPart == "*" {
 47				break
 48			}
 49			if strings.HasPrefix(patPart, "{") && strings.HasSuffix(patPart, "}") {
 50				continue
 51			}
 52			if patPart != reqPart {
 53				match = false
 54				break
 55			}
 56		}
 57		if match {
 58			req := &Request{
 59				Path:        clearPath,
 60				RawPath:     reqPath,
 61				HandlerPath: route.Pattern,
 62				Query:       query,
 63			}
 64			res := &ResponseWriter{}
 65			if route.Fn == nil {
 66				panic("Router.Render: route " + route.Pattern + " was registered via HandleFuncRlm; use RenderRlm to dispatch")
 67			}
 68			route.Fn(res, req)
 69			return res.Output()
 70		}
 71	}
 72
 73	// not found
 74	req := &Request{Path: reqPath, Query: query}
 75	res := &ResponseWriter{}
 76	r.NotFoundHandler(res, req)
 77	return res.Output()
 78}
 79
 80// RenderRlm is the rlm-aware counterpart of Render. Dispatches matched
 81// routes registered via HandleFuncRlm with the supplied rlm; routes
 82// registered via the legacy HandleFunc still work — rlm is ignored.
 83// Use this when the router carries any rlm-aware handlers.
 84func (r *Router) RenderRlm(_ int, rlm realm, reqPath string) string {
 85	clearPath, rawQuery, _ := strings.Cut(reqPath, "?")
 86	query, _ := url.ParseQuery(rawQuery)
 87	reqParts := strings.Split(clearPath, "/")
 88
 89	for _, route := range r.routes {
 90		patParts := strings.Split(route.Pattern, "/")
 91		wildcard := false
 92		for _, part := range patParts {
 93			if part == "*" {
 94				wildcard = true
 95				break
 96			}
 97		}
 98		if !wildcard && len(patParts) != len(reqParts) {
 99			continue
100		}
101
102		match := true
103		for i := 0; i < len(patParts); i++ {
104			patPart := patParts[i]
105			reqPart := reqParts[i]
106
107			if patPart == "*" {
108				break
109			}
110			if strings.HasPrefix(patPart, "{") && strings.HasSuffix(patPart, "}") {
111				continue
112			}
113			if patPart != reqPart {
114				match = false
115				break
116			}
117		}
118		if match {
119			req := &Request{
120				Path:        clearPath,
121				RawPath:     reqPath,
122				HandlerPath: route.Pattern,
123				Query:       query,
124			}
125			res := &ResponseWriter{}
126			if route.FnRlm != nil {
127				route.FnRlm(0, rlm, res, req)
128			} else {
129				route.Fn(res, req)
130			}
131			return res.Output()
132		}
133	}
134
135	// not found
136	req := &Request{Path: reqPath, Query: query}
137	res := &ResponseWriter{}
138	r.NotFoundHandler(res, req)
139	return res.Output()
140}
141
142// HandleFunc registers a route and its handler function.
143func (r *Router) HandleFunc(pattern string, fn HandlerFunc) {
144	route := Handler{Pattern: pattern, Fn: fn}
145	r.routes = append(r.routes, route)
146}
147
148// HandleFuncRlm registers a route with a rlm-aware handler. Dispatch
149// must use Router.RenderRlm — calling Router.Render on a route registered
150// via HandleFuncRlm panics (no rlm to supply).
151func (r *Router) HandleFuncRlm(pattern string, fn HandlerFuncRlm) {
152	route := Handler{Pattern: pattern, FnRlm: fn}
153	r.routes = append(r.routes, route)
154}
155
156// HandleErrFunc registers a route and its error handler function.
157func (r *Router) HandleErrFunc(pattern string, fn ErrHandlerFunc) {
158	// Convert ErrHandlerFunc to regular HandlerFunc
159	handler := func(res *ResponseWriter, req *Request) {
160		if err := fn(res, req); err != nil {
161			res.Write("Error: " + err.Error())
162		}
163	}
164
165	r.HandleFunc(pattern, handler)
166}
167
168// SetNotFoundHandler sets custom message for 404 defaultNotFoundHandler.
169func (r *Router) SetNotFoundHandler(handler NotFoundHandler) {
170	r.NotFoundHandler = handler
171}