use std::collections::HashMap;
use std::sync::Arc;
use matchit::Router as MatchitRouter;
use crate::handler::{BoxedHandler, Handler};
use crate::method::Method;
use crate::middleware::{BoxedMiddleware, IntoMiddlewares, Middleware};
pub struct Router {
routes: HashMap<Method, MatchitRouter<(BoxedHandler, Arc<[BoxedMiddleware]>)>>,
raw: Vec<(Method, Box<str>, BoxedHandler, Arc<[BoxedMiddleware]>)>,
middleware: Vec<BoxedMiddleware>,
}
impl Router {
pub fn new() -> Self {
Self { routes: HashMap::new(), raw: Vec::new(), middleware: Vec::new() }
}
pub fn middleware(mut self, mw: impl Middleware) -> Self {
self.middleware.push(mw.into_boxed_middleware());
self
}
pub fn on(
self,
method: Method,
path: &str,
handler: impl Handler,
extra: impl IntoMiddlewares,
) -> Self {
let mut chain = self.middleware.clone();
chain.extend(extra.into_middlewares());
let chain: Arc<[BoxedMiddleware]> = chain.into();
self.add_route(method, path, handler.into_boxed_handler(), chain)
}
pub fn merge(mut self, other: Router) -> Self {
for (method, path, handler, chain) in other.raw {
self = self.add_route(method, &path, handler, chain);
}
self
}
fn add_route(
mut self,
method: Method,
path: &str,
handler: BoxedHandler,
chain: Arc<[BoxedMiddleware]>,
) -> Self {
self.routes
.entry(method)
.or_default()
.insert(path, (Arc::clone(&handler), Arc::clone(&chain)))
.unwrap_or_else(|e| panic!("invalid route `{path}`: {e}"));
self.raw.push((method, path.into(), handler, chain));
self
}
pub(crate) fn lookup(
&self,
method: Method,
path: &str,
) -> Option<(BoxedHandler, Arc<[BoxedMiddleware]>, HashMap<String, String>)> {
let tree = self.routes.get(&method)?;
let matched = tree.at(path).ok()?;
let handler = Arc::clone(&matched.value.0);
let chain = Arc::clone(&matched.value.1);
let params = matched
.params
.iter()
.map(|(k, v)| (k.to_owned(), v.to_owned()))
.collect();
Some((handler, chain, params))
}
}
impl Default for Router {
fn default() -> Self {
Self::new()
}
}