1use crate::error::{Error, Result};
4use crate::handler::DynHandler;
5use crate::openapi::{RouteInfo, RouteKind};
6use crate::request::Params;
7
8#[derive(Clone)]
10struct Entry {
11 handler: DynHandler,
12 pattern: std::sync::Arc<str>,
13}
14
15#[derive(Default)]
17pub struct Router {
18 get: matchit::Router<Entry>,
19 post: matchit::Router<Entry>,
20 put: matchit::Router<Entry>,
21 delete: matchit::Router<Entry>,
22 patch: matchit::Router<Entry>,
23 registered: Vec<RouteInfo>,
24}
25
26impl Router {
27 pub fn new() -> Self {
28 Self::default()
29 }
30
31 pub fn insert(&mut self, method: &str, path: &str, handler: DynHandler) -> Result<()> {
32 self.insert_as(RouteKind::Http, method, path, handler)
33 }
34
35 pub(crate) fn insert_as(
36 &mut self,
37 kind: RouteKind,
38 method: &str,
39 path: &str,
40 handler: DynHandler,
41 ) -> Result<()> {
42 let method = method.to_uppercase();
43 let router = match method.as_str() {
44 "GET" => &mut self.get,
45 "POST" => &mut self.post,
46 "PUT" => &mut self.put,
47 "DELETE" => &mut self.delete,
48 "PATCH" => &mut self.patch,
49 _ => {
50 return Err(Error::bad_request(format!(
51 "Unsupported method: {}",
52 method
53 )))
54 }
55 };
56
57 router
58 .insert(
59 path,
60 Entry {
61 handler,
62 pattern: path.into(),
63 },
64 )
65 .map_err(|e| Error::internal(format!("Failed to insert route: {}", e)))?;
66
67 self.registered.push(RouteInfo {
68 method,
69 path: path.to_string(),
70 kind,
71 });
72 Ok(())
73 }
74
75 pub fn allowed_methods(&self, path: &str) -> Vec<&'static str> {
78 let tables: [(&'static str, &matchit::Router<Entry>); 5] = [
79 ("GET", &self.get),
80 ("POST", &self.post),
81 ("PUT", &self.put),
82 ("DELETE", &self.delete),
83 ("PATCH", &self.patch),
84 ];
85 let mut allowed = Vec::new();
86 for (name, table) in tables {
87 if table.at(path).is_ok() {
88 allowed.push(name);
89 if name == "GET" {
90 allowed.push("HEAD");
91 }
92 }
93 }
94 if !allowed.is_empty() {
95 allowed.push("OPTIONS");
96 }
97 allowed
98 }
99
100 pub(crate) fn registered(&self) -> &[RouteInfo] {
101 &self.registered
102 }
103
104 pub fn match_route(&self, method: &str, path: &str) -> Result<(DynHandler, Params)> {
105 self.match_route_with_pattern(method, path)
106 .map(|(handler, params, _)| (handler, params))
107 }
108
109 pub fn match_route_with_pattern(
112 &self,
113 method: &str,
114 path: &str,
115 ) -> Result<(DynHandler, Params, std::sync::Arc<str>)> {
116 let router = match method.to_uppercase().as_str() {
117 "GET" | "HEAD" => &self.get,
119 "POST" => &self.post,
120 "PUT" => &self.put,
121 "DELETE" => &self.delete,
122 "PATCH" => &self.patch,
123 _ => return Err(Error::not_found("Route not found")),
124 };
125
126 let matched = router
127 .at(path)
128 .map_err(|_| Error::not_found("Route not found"))?;
129
130 let mut params = Params::new();
131 for (key, value) in matched.params.iter() {
132 params.insert(key.to_string(), value.to_string());
133 }
134
135 Ok((
136 matched.value.handler.clone(),
137 params,
138 matched.value.pattern.clone(),
139 ))
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::handler::Handler;
147 use crate::request::Request;
148 use crate::response::Response;
149 use std::sync::Arc;
150
151 struct TestHandler;
152
153 #[async_trait::async_trait]
154 impl Handler for TestHandler {
155 async fn call(&self, _req: Request) -> Result<Response> {
156 Ok(Response::text("test"))
157 }
158 }
159
160 #[test]
161 fn test_router_insert_and_match() {
162 let mut router = Router::new();
163 let handler = Arc::new(TestHandler) as DynHandler;
164
165 router
166 .insert("GET", "/hello/{name}", handler.clone())
167 .unwrap();
168
169 let (_, params) = router.match_route("GET", "/hello/world").unwrap();
170 assert_eq!(params.get("name").unwrap(), "world");
171 }
172
173 #[test]
174 fn allowed_methods_lists_what_the_path_supports() {
175 let mut router = Router::new();
176 let handler = Arc::new(TestHandler) as DynHandler;
177 router
178 .insert("GET", "/users/{id}", handler.clone())
179 .unwrap();
180 router
181 .insert("DELETE", "/users/{id}", handler.clone())
182 .unwrap();
183 router.insert("POST", "/users", handler.clone()).unwrap();
184
185 assert_eq!(
186 router.allowed_methods("/users/7"),
187 ["GET", "HEAD", "DELETE", "OPTIONS"]
188 );
189 assert_eq!(router.allowed_methods("/users"), ["POST", "OPTIONS"]);
190 assert!(router.allowed_methods("/nothing").is_empty());
191 assert!(router.match_route("HEAD", "/users/7").is_ok());
193 assert!(router.match_route("HEAD", "/users").is_err());
194 }
195
196 #[test]
197 fn test_router_not_found() {
198 let router = Router::new();
199 assert!(router.match_route("GET", "/not-found").is_err());
200 }
201}