1use std::collections::HashMap;
2
3use hyper::Method;
4
5use crate::handler::Handler;
6
7#[derive(Clone, Debug, Default)]
13pub struct PathParams(pub HashMap<String, String>);
14
15#[derive(Clone, Debug, Default)]
21pub struct QueryParams(pub HashMap<String, String>);
22
23#[derive(Default)]
28pub struct Router<S> {
29 root: Node<S>,
30}
31
32struct Node<S> {
33 segment: String,
34 param_name: String,
35 is_wildcard: bool,
36 handlers: HashMap<Method, Handler<S>>,
37 children: Vec<Node<S>>,
38}
39
40impl<S> Default for Node<S> {
41 fn default() -> Self {
42 Node {
43 segment: String::new(),
44 param_name: String::new(),
45 is_wildcard: false,
46 handlers: HashMap::new(),
47 children: Vec::new(),
48 }
49 }
50}
51
52impl<S: Send + Sync + 'static> Router<S> {
53 pub fn new() -> Self {
55 Router { root: Node::default() }
56 }
57
58 pub fn insert(&mut self, method: Method, path: &str, handler: Handler<S>) {
63 let segments = split_path(path);
64 let mut node = &mut self.root;
65
66 for seg in segments {
67 if seg == "*" {
68 if let Some(idx) = node.children.iter().position(|c| c.is_wildcard) {
69 node = &mut node.children[idx];
70 } else {
71 node.children.push(Node {
72 segment: "*".to_string(),
73 param_name: String::new(),
74 is_wildcard: true,
75 handlers: HashMap::new(),
76 children: Vec::new(),
77 });
78 node = node.children.last_mut().unwrap();
79 }
80 } else if let Some(param_name) = seg.strip_prefix(':') {
81 if let Some(idx) = node.children.iter().position(|c| c.param_name == param_name) {
82 node = &mut node.children[idx];
83 } else {
84 node.children.push(Node {
85 segment: seg.to_string(),
86 param_name: param_name.to_string(),
87 is_wildcard: false,
88 handlers: HashMap::new(),
89 children: Vec::new(),
90 });
91 node = node.children.last_mut().unwrap();
92 }
93 } else {
94 if let Some(idx) = node.children.iter().position(|c| c.segment == seg) {
95 node = &mut node.children[idx];
96 } else {
97 node.children.push(Node {
98 segment: seg.to_string(),
99 param_name: String::new(),
100 is_wildcard: false,
101 handlers: HashMap::new(),
102 children: Vec::new(),
103 });
104 node = node.children.last_mut().unwrap();
105 }
106 }
107 }
108
109 node.handlers.insert(method, handler);
110 }
111
112 pub fn match_route<'a>(
117 &'a self,
118 method: &Method,
119 path: &str,
120 ) -> Option<(&'a Handler<S>, PathParams)> {
121 let segments = split_path(path);
122 let mut params = PathParams::default();
123 let node = Self::find_node(&self.root, &segments, 0, method, &mut params)?;
124 node.handlers.get(method).map(|h| (h, params))
125 }
126
127 pub fn allowed_methods(&self, path: &str) -> Vec<Method> {
131 let segments = split_path(path);
132 let mut methods = std::collections::HashSet::new();
133 let mut params = PathParams::default();
134 Self::collect_allowed_methods(&self.root, &segments, 0, &mut params, &mut methods);
135 methods.into_iter().collect()
136 }
137
138 pub fn path_exists(&self, path: &str) -> bool {
140 !self.allowed_methods(path).is_empty()
141 }
142
143 fn find_node<'a>(
157 node: &'a Node<S>,
158 segments: &[String],
159 idx: usize,
160 method: &Method,
161 params: &mut PathParams,
162 ) -> Option<&'a Node<S>> {
163 if idx == segments.len() {
164 return if node.handlers.contains_key(method) { Some(node) } else { None };
165 }
166
167 let seg = &segments[idx];
168
169 for child in &node.children {
170 if !child.is_wildcard && child.param_name.is_empty() && child.segment == *seg {
171 if let Some(found) = Self::find_node(child, segments, idx + 1, method, params) {
173 return Some(found);
174 }
175 }
176 }
177
178 for child in &node.children {
179 if !child.is_wildcard && !child.param_name.is_empty() {
180 let mut p = params.clone();
182 p.0.insert(child.param_name.clone(), seg.clone());
183 if let Some(found) = Self::find_node(child, segments, idx + 1, method, &mut p) {
184 *params = p;
185 return Some(found);
186 }
187 params.0.remove(&child.param_name);
189 }
190 }
191
192 for child in &node.children {
193 if child.is_wildcard && child.handlers.contains_key(method) {
194 params.0.insert("*".to_string(), segments[idx..].join("/"));
195 return Some(child);
196 }
197 }
198
199 None
200 }
201
202 fn collect_allowed_methods(
209 node: &Node<S>,
210 segments: &[String],
211 idx: usize,
212 params: &mut PathParams,
213 methods: &mut std::collections::HashSet<Method>,
214 ) {
215 if idx == segments.len() {
216 methods.extend(node.handlers.keys().cloned());
217 return;
218 }
219
220 let seg = &segments[idx];
221
222 for child in &node.children {
223 if !child.is_wildcard && child.param_name.is_empty() && child.segment == *seg {
224 let mut p = params.clone();
225 Self::collect_allowed_methods(child, segments, idx + 1, &mut p, methods);
226 }
227 }
228
229 for child in &node.children {
230 if !child.is_wildcard && !child.param_name.is_empty() {
231 let mut p = params.clone();
232 p.0.insert(child.param_name.clone(), seg.clone());
233 Self::collect_allowed_methods(child, segments, idx + 1, &mut p, methods);
234 }
235 }
236
237 for child in &node.children {
238 if child.is_wildcard {
239 methods.extend(child.handlers.keys().cloned());
240 }
241 }
242 }
243}
244
245fn split_path(path: &str) -> Vec<String> {
246 path.trim_start_matches('/')
247 .split('/')
248 .filter(|s| !s.is_empty())
249 .map(|s| percent_encoding::percent_decode_str(s).decode_utf8_lossy().into_owned())
250 .collect()
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::error::ServeError;
257 use hyper::Response;
258 use hyper::body::Bytes;
259
260 fn dummy_handler() -> Handler<()> {
261 crate::handler::handler(|_, _| async {
262 Ok::<_, ServeError>(Response::new(crate::handler::body(Bytes::from("ok"))))
263 })
264 }
265
266 #[test]
267 fn insert_and_match_static() {
268 let mut router = Router::new();
269 router.insert(Method::GET, "/hello", dummy_handler());
270 let (_, _) = router.match_route(&Method::GET, "/hello").unwrap();
271 }
272
273 #[test]
274 fn match_with_path_param() {
275 let mut router = Router::new();
276 router.insert(Method::GET, "/users/:id", dummy_handler());
277 let (_, params) = router.match_route(&Method::GET, "/users/42").unwrap();
278 assert_eq!(params.0.get("id").unwrap(), "42");
279 }
280
281 #[test]
282 fn match_with_wildcard() {
283 let mut router = Router::new();
284 router.insert(Method::GET, "/files/*", dummy_handler());
285 let (_, params) = router.match_route(&Method::GET, "/files/a/b/c").unwrap();
286 assert_eq!(params.0.get("*").unwrap(), "a/b/c");
287 }
288
289 #[test]
290 fn no_match_for_unregistered_route() {
291 let mut router = Router::new();
292 router.insert(Method::GET, "/hello", dummy_handler());
293 assert!(router.match_route(&Method::GET, "/world").is_none());
294 }
295
296 #[test]
297 fn method_mismatch_returns_none() {
298 let mut router = Router::new();
299 router.insert(Method::GET, "/hello", dummy_handler());
300 assert!(router.match_route(&Method::POST, "/hello").is_none());
301 }
302
303 #[test]
304 fn root_path_matches() {
305 let mut router = Router::new();
306 router.insert(Method::GET, "/", dummy_handler());
307 let (_, _) = router.match_route(&Method::GET, "/").unwrap();
308 }
309
310 #[test]
311 fn method_mismatch_on_static_branch_backtracks_to_param_sibling() {
312 let mut router = Router::new();
313 router.insert(Method::GET, "/users/:id", dummy_handler());
314 router.insert(Method::POST, "/users/new", dummy_handler());
315
316 let (_, params) = router.match_route(&Method::GET, "/users/new").unwrap();
320 assert_eq!(params.0.get("id").unwrap(), "new");
321 }
322
323 #[test]
324 fn static_branch_with_matching_method_still_wins_over_param_sibling() {
325 let mut router = Router::new();
326 router.insert(Method::GET, "/users/:id", dummy_handler());
327 router.insert(Method::GET, "/users/new", dummy_handler());
328
329 let (_, params) = router.match_route(&Method::GET, "/users/new").unwrap();
330 assert!(params.0.is_empty(), "static branch should win, not fall back to :id");
331 }
332
333 #[test]
334 fn allowed_methods_unions_across_ambiguous_branches() {
335 let mut router = Router::new();
336 router.insert(Method::GET, "/users/:id", dummy_handler());
337 router.insert(Method::POST, "/users/new", dummy_handler());
338
339 let mut allowed = router.allowed_methods("/users/new");
340 allowed.sort_by_key(|m| m.to_string());
341 assert_eq!(allowed, vec![Method::GET, Method::POST]);
342 }
343
344 #[test]
345 fn static_path_traversal_borrows_params_without_cloning() {
346 let mut router = Router::new();
347 router.insert(Method::GET, "/api/v1/users/profile", dummy_handler());
349 let (_, params) = router.match_route(&Method::GET, "/api/v1/users/profile").unwrap();
350
351 assert!(params.0.is_empty());
355 }
356
357 #[test]
358 fn param_backtracking_truncates_params_on_failure() {
359 let mut router = Router::new();
360 router.insert(Method::GET, "/users/:id", dummy_handler());
362 router.insert(Method::POST, "/users/new", dummy_handler());
364
365 let (_, params) = router.match_route(&Method::GET, "/users/new").unwrap();
367 assert_eq!(params.0.get("id").unwrap(), "new");
368
369 assert_eq!(params.0.len(), 1);
371 }
372}