Skip to main content

ullun/
router.rs

1//! Router implementation using matchit
2
3use crate::error::{Error, Result};
4use crate::handler::DynHandler;
5use crate::request::Params;
6
7/// HTTP method routing
8#[derive(Default)]
9pub struct Router {
10    get: matchit::Router<DynHandler>,
11    post: matchit::Router<DynHandler>,
12    put: matchit::Router<DynHandler>,
13    delete: matchit::Router<DynHandler>,
14    patch: matchit::Router<DynHandler>,
15}
16
17impl Router {
18    pub fn new() -> Self {
19        Self::default()
20    }
21
22    pub fn insert(&mut self, method: &str, path: &str, handler: DynHandler) -> Result<()> {
23        let router = match method.to_uppercase().as_str() {
24            "GET" => &mut self.get,
25            "POST" => &mut self.post,
26            "PUT" => &mut self.put,
27            "DELETE" => &mut self.delete,
28            "PATCH" => &mut self.patch,
29            _ => {
30                return Err(Error::bad_request(format!(
31                    "Unsupported method: {}",
32                    method
33                )))
34            }
35        };
36
37        router
38            .insert(path, handler)
39            .map_err(|e| Error::internal(format!("Failed to insert route: {}", e)))?;
40
41        Ok(())
42    }
43
44    pub fn match_route(&self, method: &str, path: &str) -> Result<(DynHandler, Params)> {
45        let router = match method.to_uppercase().as_str() {
46            "GET" => &self.get,
47            "POST" => &self.post,
48            "PUT" => &self.put,
49            "DELETE" => &self.delete,
50            "PATCH" => &self.patch,
51            _ => return Err(Error::not_found("Route not found")),
52        };
53
54        let matched = router
55            .at(path)
56            .map_err(|_| Error::not_found("Route not found"))?;
57
58        let mut params = Params::new();
59        for (key, value) in matched.params.iter() {
60            params.insert(key.to_string(), value.to_string());
61        }
62
63        Ok((matched.value.clone(), params))
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use crate::handler::Handler;
71    use crate::request::Request;
72    use crate::response::Response;
73    use std::sync::Arc;
74
75    struct TestHandler;
76
77    #[async_trait::async_trait]
78    impl Handler for TestHandler {
79        async fn call(&self, _req: Request) -> Result<Response> {
80            Ok(Response::text("test"))
81        }
82    }
83
84    #[test]
85    fn test_router_insert_and_match() {
86        let mut router = Router::new();
87        let handler = Arc::new(TestHandler) as DynHandler;
88
89        router
90            .insert("GET", "/hello/{name}", handler.clone())
91            .unwrap();
92
93        let (_, params) = router.match_route("GET", "/hello/world").unwrap();
94        assert_eq!(params.get("name").unwrap(), "world");
95    }
96
97    #[test]
98    fn test_router_not_found() {
99        let router = Router::new();
100        assert!(router.match_route("GET", "/not-found").is_err());
101    }
102}