1use std::{collections::HashMap, sync::Arc};
2
3type RouteParams = HashMap<String, String>;
5
6#[derive(Clone)]
8pub struct RouteMatch {
9 pub path: String,
11 pub params: RouteParams,
13 pub query: HashMap<String, String>,
15}
16
17pub trait RouteHandler: Send + Sync {
19 fn handle(&self, params: &RouteParams, query: &HashMap<String, String>) -> Result<String, String>;
28}
29
30pub struct Router {
32 routes: HashMap<String, Arc<dyn RouteHandler>>,
34 route_cache: HashMap<String, RouteMatch>,
36}
37
38impl Router {
39 pub fn new() -> Self {
41 Self { routes: HashMap::new(), route_cache: HashMap::new() }
42 }
43
44 pub fn add_route<H: RouteHandler + 'static>(&mut self, path: &str, handler: H) {
50 self.routes.insert(path.to_string(), Arc::new(handler));
51 }
52
53 pub fn match_route(&mut self, path: &str) -> Option<(Arc<dyn RouteHandler>, RouteMatch)> {
61 if let Some(match_result) = self.route_cache.get(path) {
63 if let Some(handler) = self.routes.get(&match_result.path) {
65 return Some((handler.clone(), match_result.clone()));
66 }
67 }
68
69 let (path_part, query_part) = path.split_once('?').unwrap_or((path, ""));
71 let query = self.parse_query(query_part);
72
73 for (route_path, handler) in &self.routes {
75 if let Some(params) = self.match_path(route_path, path_part) {
76 let match_result = RouteMatch { path: route_path.clone(), params, query };
77 self.route_cache.insert(path.to_string(), match_result.clone());
79 return Some((handler.clone(), match_result));
80 }
81 }
82
83 None
84 }
85
86 fn parse_query(&self, query_str: &str) -> HashMap<String, String> {
94 let mut query = HashMap::new();
95 for pair in query_str.split('&') {
96 if let Some((key, value)) = pair.split_once('=') {
97 query.insert(key.to_string(), value.to_string());
98 }
99 }
100 query
101 }
102
103 fn match_path(&self, route_path: &str, request_path: &str) -> Option<RouteParams> {
112 let route_parts: Vec<&str> = route_path.split('/').filter(|p| !p.is_empty()).collect();
113 let request_parts: Vec<&str> = request_path.split('/').filter(|p| !p.is_empty()).collect();
114
115 if route_parts.len() != request_parts.len() {
116 return None;
117 }
118
119 let mut params = RouteParams::new();
120 for (route_part, request_part) in route_parts.iter().zip(request_parts.iter()) {
121 if route_part.starts_with(':') {
122 let param_name = route_part.trim_start_matches(':');
124 params.insert(param_name.to_string(), request_part.to_string());
125 }
126 else if route_part != request_part {
127 return None;
129 }
130 }
131
132 Some(params)
133 }
134
135 pub fn clear_cache(&mut self) {
137 self.route_cache.clear();
138 }
139}