1use std::collections::HashMap;
2
3use hyper::Method;
4
5use crate::handler::Handler;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct PathSegments(pub Vec<String>);
17
18#[derive(Clone, Debug, Default)]
22pub struct PathParams(pub HashMap<String, String>);
23
24#[derive(Clone, Debug, Default)]
31pub struct QueryParams(pub HashMap<String, String>);
32
33#[derive(Default)]
38pub struct Router<S> {
39 root: Node<S>,
40}
41
42struct Node<S> {
43 segment: String,
44 param_name: String,
45 is_wildcard: bool,
46 handlers: Vec<(Method, Handler<S>)>,
56 children: Vec<Node<S>>,
57}
58
59impl<S> Default for Node<S> {
60 fn default() -> Self {
61 Node {
62 segment: String::new(),
63 param_name: String::new(),
64 is_wildcard: false,
65 handlers: Vec::new(),
66 children: Vec::new(),
67 }
68 }
69}
70
71impl<S: Send + Sync + 'static> Router<S> {
72 pub fn new() -> Self {
74 Router { root: Node::default() }
75 }
76
77 pub fn insert(&mut self, method: Method, path: &str, handler: Handler<S>) {
82 let segments = split_path(path);
83 let mut node = &mut self.root;
84
85 for seg in segments {
86 if seg == "*" {
87 if let Some(idx) = node.children.iter().position(|c| c.is_wildcard) {
88 node = &mut node.children[idx];
89 } else {
90 node.children.push(Node {
91 segment: "*".to_string(),
92 param_name: String::new(),
93 is_wildcard: true,
94 handlers: Vec::new(),
95 children: Vec::new(),
96 });
97 node = node.children.last_mut().unwrap();
98 }
99 } else if let Some(param_name) = seg.strip_prefix(':') {
100 if let Some(idx) = node.children.iter().position(|c| c.param_name == param_name) {
101 node = &mut node.children[idx];
102 } else {
103 node.children.push(Node {
104 segment: seg.to_string(),
105 param_name: param_name.to_string(),
106 is_wildcard: false,
107 handlers: Vec::new(),
108 children: Vec::new(),
109 });
110 node = node.children.last_mut().unwrap();
111 }
112 } else {
113 if let Some(idx) = node.children.iter().position(|c| c.segment == seg) {
114 node = &mut node.children[idx];
115 } else {
116 node.children.push(Node {
117 segment: seg.to_string(),
118 param_name: String::new(),
119 is_wildcard: false,
120 handlers: Vec::new(),
121 children: Vec::new(),
122 });
123 node = node.children.last_mut().unwrap();
124 }
125 }
126 }
127
128 if let Some(slot) = node.handlers.iter_mut().find(|(m, _)| *m == method) {
132 slot.1 = handler;
133 } else {
134 node.handlers.push((method, handler));
135 }
136 }
137
138 #[cfg(test)]
145 pub fn match_route<'a>(
146 &'a self,
147 method: &Method,
148 path: &str,
149 ) -> Option<(&'a Handler<S>, PathParams)> {
150 self.match_segments(method, &split_path(path))
151 }
152
153 pub(crate) fn match_segments<'a>(
161 &'a self,
162 method: &Method,
163 segments: &[String],
164 ) -> Option<(&'a Handler<S>, PathParams)> {
165 let mut params = PathParams::default();
166 let node = Self::find_node(&self.root, segments, 0, method, &mut params)?;
167 node.handlers.iter().find(|(m, _)| m == method).map(|(_, h)| (h, params))
168 }
169
170 #[cfg(test)]
176 pub fn allowed_methods(&self, path: &str) -> Vec<Method> {
177 self.allowed_methods_for(&split_path(path))
178 }
179
180 pub(crate) fn allowed_methods_for(&self, segments: &[String]) -> Vec<Method> {
182 let mut methods = std::collections::HashSet::new();
183 let mut params = PathParams::default();
184 Self::collect_allowed_methods(&self.root, segments, 0, &mut params, &mut methods);
185 methods.into_iter().collect()
186 }
187
188 pub(crate) fn segments_exist(&self, segments: &[String]) -> bool {
190 !self.allowed_methods_for(segments).is_empty()
191 }
192
193 fn find_node<'a>(
207 node: &'a Node<S>,
208 segments: &[String],
209 idx: usize,
210 method: &Method,
211 params: &mut PathParams,
212 ) -> Option<&'a Node<S>> {
213 if idx == segments.len() {
214 return if node.handlers.iter().any(|(m, _)| m == method) { Some(node) } else { None };
215 }
216
217 let seg = &segments[idx];
218
219 for child in &node.children {
220 if !child.is_wildcard && child.param_name.is_empty() && child.segment == *seg {
221 if let Some(found) = Self::find_node(child, segments, idx + 1, method, params) {
223 return Some(found);
224 }
225 }
226 }
227
228 for child in &node.children {
229 if !child.is_wildcard && !child.param_name.is_empty() {
230 let mut p = params.clone();
232 p.0.insert(child.param_name.clone(), seg.clone());
233 if let Some(found) = Self::find_node(child, segments, idx + 1, method, &mut p) {
234 *params = p;
235 return Some(found);
236 }
237 params.0.remove(&child.param_name);
239 }
240 }
241
242 for child in &node.children {
243 if child.is_wildcard && child.handlers.iter().any(|(m, _)| m == method) {
244 params.0.insert("*".to_string(), segments[idx..].join("/"));
245 return Some(child);
246 }
247 }
248
249 None
250 }
251
252 fn collect_allowed_methods(
259 node: &Node<S>,
260 segments: &[String],
261 idx: usize,
262 params: &mut PathParams,
263 methods: &mut std::collections::HashSet<Method>,
264 ) {
265 if idx == segments.len() {
266 methods.extend(node.handlers.iter().map(|(m, _)| m.clone()));
267 return;
268 }
269
270 let seg = &segments[idx];
271
272 for child in &node.children {
273 if !child.is_wildcard && child.param_name.is_empty() && child.segment == *seg {
274 let mut p = params.clone();
275 Self::collect_allowed_methods(child, segments, idx + 1, &mut p, methods);
276 }
277 }
278
279 for child in &node.children {
280 if !child.is_wildcard && !child.param_name.is_empty() {
281 let mut p = params.clone();
282 p.0.insert(child.param_name.clone(), seg.clone());
283 Self::collect_allowed_methods(child, segments, idx + 1, &mut p, methods);
284 }
285 }
286
287 for child in &node.children {
288 if child.is_wildcard {
289 methods.extend(child.handlers.iter().map(|(m, _)| m.clone()));
290 }
291 }
292 }
293}
294
295pub(crate) fn split_path(path: &str) -> Vec<String> {
296 path.trim_start_matches('/')
297 .split('/')
298 .filter(|s| !s.is_empty())
299 .map(|s| percent_encoding::percent_decode_str(s).decode_utf8_lossy().into_owned())
300 .collect()
301}
302
303#[cfg(test)]
304#[path = "../tests/unit/router.rs"]
305mod tests;
306
307#[cfg(test)]
308#[path = "../tests/unit/router_properties.rs"]
309mod property_tests;