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}