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