use super::{PathMatcher, PathPattern, Route, Router};
use async_trait::async_trait;
use reinhardt_http::{Handler, Request, Response, Result};
pub struct SimpleRouter {
routes: Vec<Route>,
matcher: PathMatcher,
}
impl SimpleRouter {
pub fn new() -> Self {
Self {
routes: Vec::new(),
matcher: PathMatcher::new(),
}
}
pub fn get_routes(&self) -> &[Route] {
&self.routes
}
}
impl Default for SimpleRouter {
fn default() -> Self {
Self::new()
}
}
impl super::router::Router for SimpleRouter {
fn add_route(&mut self, route: Route) {
let pattern = PathPattern::new(&route.path).expect("Invalid path pattern");
let handler_id = route
.name
.clone()
.unwrap_or_else(|| format!("route_{}", self.routes.len()));
if let Err(e) = self.matcher.add_pattern(pattern, handler_id) {
tracing::warn!(
"SimpleRouter: failed to register pattern for path '{}': {}",
route.path,
e
);
return;
}
self.routes.push(route);
}
fn mount(&mut self, prefix: &str, routes: Vec<Route>, _namespace: Option<String>) {
let prefix = prefix.trim_end_matches('/');
for mut route in routes {
let new_path = if route.path.starts_with('/') {
format!("{}{}", prefix, route.path)
} else {
format!("{}/{}", prefix, route.path)
};
route.path = new_path;
self.add_route(route);
}
}
async fn route(&self, mut request: Request) -> Result<Response> {
let path = request.path().to_string();
if let Some((handler_id, params)) = self.matcher.match_path(&path) {
for (idx, route) in self.routes.iter().enumerate() {
let route_id = route.name.as_deref().unwrap_or({
""
});
let expected_id = if route_id.is_empty() {
format!("route_{}", idx)
} else {
route_id.to_string()
};
if expected_id == handler_id {
request.path_params = params;
return route.handler().handle(request).await;
}
}
}
Err(reinhardt_core::exception::Error::NotFound(format!(
"No route found for {}",
path
)))
}
}
#[async_trait]
impl Handler for SimpleRouter {
async fn handle(&self, request: Request) -> Result<Response> {
self.route(request).await
}
}
#[cfg(test)]
mod tests {
use super::Router;
use super::*;
use crate::routers::helpers::path;
use async_trait::async_trait;
use bytes::Bytes;
use hyper::{HeaderMap, Method, Uri, Version};
use reinhardt_http::{Request, Response, Result};
struct DummyHandler;
#[async_trait]
impl Handler for DummyHandler {
async fn handle(&self, _req: Request) -> Result<Response> {
Ok(Response::ok())
}
}
#[test]
fn test_simple_router_new() {
let router = SimpleRouter::new();
assert_eq!(router.get_routes().len(), 0);
}
#[test]
fn test_simple_router_add_route() {
let mut router = SimpleRouter::new();
let handler = std::sync::Arc::new(DummyHandler);
router.add_route(path("/users/", handler.clone()));
router.add_route(path("/users/{id}/", handler));
assert_eq!(router.get_routes().len(), 2);
assert_eq!(router.get_routes()[0].path, "/users/");
assert_eq!(router.get_routes()[1].path, "/users/{id}/");
}
#[test]
fn test_simple_router_mount() {
let mut router = SimpleRouter::new();
let handler = std::sync::Arc::new(DummyHandler);
let users_routes = vec![path("/", handler.clone()), path("/{id}/", handler)];
router.mount("/users", users_routes, None);
assert_eq!(router.get_routes().len(), 2);
assert_eq!(router.get_routes()[0].path, "/users/");
assert_eq!(router.get_routes()[1].path, "/users/{id}/");
}
#[tokio::test]
async fn test_simple_router_route() {
let mut router = SimpleRouter::new();
let handler = std::sync::Arc::new(DummyHandler);
let mut users_route = path("/users/", handler.clone());
users_route.name = Some("users".to_string());
router.add_route(users_route);
let mut detail_route = path("/users/{id}/", handler);
detail_route.name = Some("user-detail".to_string());
router.add_route(detail_route);
let uri = Uri::from_static("/users/");
let req = Request::builder()
.method(Method::GET)
.uri(uri)
.version(Version::HTTP_11)
.headers(HeaderMap::new())
.body(Bytes::new())
.build()
.unwrap();
let response = router.route(req).await.unwrap();
assert_eq!(response.status, 200);
}
#[tokio::test]
async fn test_simple_router_route_with_params() {
let mut router = SimpleRouter::new();
let handler = std::sync::Arc::new(DummyHandler);
let mut detail_route = path("/users/{id}/", handler);
detail_route.name = Some("user-detail".to_string());
router.add_route(detail_route);
let uri = Uri::from_static("/users/123/");
let req = Request::builder()
.method(Method::GET)
.uri(uri)
.version(Version::HTTP_11)
.headers(HeaderMap::new())
.body(Bytes::new())
.build()
.unwrap();
let response = router.route(req).await.unwrap();
assert_eq!(response.status, 200);
}
#[tokio::test]
async fn test_simple_router_not_found() {
let router = SimpleRouter::new();
let uri = Uri::from_static("/nonexistent/");
let req = Request::builder()
.method(Method::GET)
.uri(uri)
.version(Version::HTTP_11)
.headers(HeaderMap::new())
.body(Bytes::new())
.build()
.unwrap();
let result = router.route(req).await;
assert!(result.is_err());
}
}