use crate::data_map::ScopedDataMap;
use crate::middleware::{PostMiddleware, PreMiddleware};
use crate::route::Route;
use crate::types::RequestInfo;
use crate::Error;
use hyper::{body::HttpBody, Request, Response};
use regex::RegexSet;
use std::fmt::{self, Debug, Formatter};
use std::future::Future;
use std::pin::Pin;
pub use self::builder::RouterBuilder;
mod builder;
pub(crate) type ErrHandlerWithoutInfo<B> =
Box<dyn FnMut(crate::Error) -> ErrHandlerWithoutInfoReturn<B> + Send + Sync + 'static>;
pub(crate) type ErrHandlerWithoutInfoReturn<B> = Box<dyn Future<Output = Response<B>> + Send + 'static>;
pub(crate) type ErrHandlerWithInfo<B> =
Box<dyn FnMut(crate::Error, RequestInfo) -> ErrHandlerWithInfoReturn<B> + Send + Sync + 'static>;
pub(crate) type ErrHandlerWithInfoReturn<B> = Box<dyn Future<Output = Response<B>> + Send + 'static>;
pub struct Router<B, E> {
pub(crate) pre_middlewares: Vec<PreMiddleware<E>>,
pub(crate) routes: Vec<Route<B, E>>,
pub(crate) post_middlewares: Vec<PostMiddleware<B, E>>,
pub(crate) scoped_data_maps: Vec<ScopedDataMap>,
pub(crate) err_handler: Option<ErrHandler<B>>,
regex_set: Option<RegexSet>,
pub(crate) should_gen_req_info: Option<bool>,
}
pub(crate) enum ErrHandler<B> {
WithoutInfo(ErrHandlerWithoutInfo<B>),
WithInfo(ErrHandlerWithInfo<B>),
}
impl<B: HttpBody + Send + Sync + Unpin + 'static> ErrHandler<B> {
pub(crate) async fn execute(&mut self, err: crate::Error, req_info: Option<RequestInfo>) -> Response<B> {
match self {
ErrHandler::WithoutInfo(ref mut err_handler) => Pin::from(err_handler(err)).await,
ErrHandler::WithInfo(ref mut err_handler) => {
Pin::from(err_handler(err, req_info.expect("No RequestInfo is provided"))).await
}
}
}
}
impl<
B: HttpBody + Send + Sync + Unpin + 'static,
E: Into<Box<dyn std::error::Error + Send + Sync>> + Unpin + 'static,
> Router<B, E>
{
pub(crate) fn new(
pre_middlewares: Vec<PreMiddleware<E>>,
routes: Vec<Route<B, E>>,
post_middlewares: Vec<PostMiddleware<B, E>>,
scoped_data_maps: Vec<ScopedDataMap>,
err_handler: Option<ErrHandler<B>>,
) -> Self {
Router {
pre_middlewares,
routes,
post_middlewares,
scoped_data_maps,
err_handler,
regex_set: None,
should_gen_req_info: None,
}
}
pub(crate) fn init_regex_set(&mut self) -> crate::Result<()> {
let regex_iter = self
.pre_middlewares
.iter()
.map(|m| m.regex.as_str())
.chain(self.routes.iter().map(|r| r.regex.as_str()))
.chain(self.post_middlewares.iter().map(|m| m.regex.as_str()))
.chain(self.scoped_data_maps.iter().map(|d| d.regex.as_str()));
self.regex_set = Some(RegexSet::new(regex_iter).map_err(Error::CreateRouterRegexSet)?);
Ok(())
}
pub(crate) fn init_req_info_gen(&mut self) -> crate::Result<()> {
if let Some(ref err_handler) = self.err_handler {
if let ErrHandler::WithInfo(_) = err_handler {
self.should_gen_req_info = Some(true);
return Ok(());
}
}
for post_middleware in self.post_middlewares.iter() {
if post_middleware.should_require_req_meta() {
self.should_gen_req_info = Some(true);
return Ok(());
}
}
self.should_gen_req_info = Some(false);
Ok(())
}
pub fn builder() -> RouterBuilder<B, E> {
builder::RouterBuilder::new()
}
pub(crate) async fn process(
&mut self,
target_path: &str,
mut req: Request<hyper::Body>,
mut req_info: Option<RequestInfo>,
) -> crate::Result<Response<B>> {
let (
matched_pre_middleware_idxs,
matched_route_idxs,
matched_post_middleware_idxs,
matched_scoped_data_map_idxs,
) = self.match_regex_set(target_path);
let shared_data_maps = matched_scoped_data_map_idxs
.into_iter()
.map(|idx| self.scoped_data_maps[idx].clone_data_map())
.collect::<Vec<_>>();
if let Some(ref mut req_info) = req_info {
if !shared_data_maps.is_empty() {
req_info.shared_data_maps.replace(Box::new(shared_data_maps.clone()));
}
}
let ext = req.extensions_mut();
ext.insert(shared_data_maps);
let mut transformed_req = req;
for idx in matched_pre_middleware_idxs {
let pre_middleware = &mut self.pre_middlewares[idx];
transformed_req = pre_middleware.process(transformed_req).await?;
}
let mut resp = None;
for idx in matched_route_idxs {
let route = &mut self.routes[idx];
if route.is_match_method(transformed_req.method()) {
let route_resp_res = route.process(target_path, transformed_req).await;
let route_resp = match route_resp_res {
Ok(route_resp) => route_resp,
Err(err) => {
if let Some(ref mut err_handler) = self.err_handler {
err_handler.execute(err, req_info.clone()).await
} else {
return Err(err);
}
}
};
resp = Some(route_resp);
break;
}
}
if resp.is_none() {
return Err(Error::HandleNonExistentRoute);
}
let mut transformed_res = resp.unwrap();
for idx in matched_post_middleware_idxs {
let post_middleware = &mut self.post_middlewares[idx];
transformed_res = post_middleware.process(transformed_res, req_info.clone()).await?;
}
Ok(transformed_res)
}
fn match_regex_set(&self, target_path: &str) -> (Vec<usize>, Vec<usize>, Vec<usize>, Vec<usize>) {
let matches = self
.regex_set
.as_ref()
.expect("The 'regex_set' field in Router is not initialized")
.matches(target_path)
.into_iter();
let pre_middlewares_len = self.pre_middlewares.len();
let routes_len = self.routes.len();
let post_middlewares_len = self.post_middlewares.len();
let scoped_data_maps_len = self.scoped_data_maps.len();
let mut matched_pre_middleware_idxs = Vec::new();
let mut matched_route_idxs = Vec::new();
let mut matched_post_middleware_idxs = Vec::new();
let mut matched_scoped_data_map_idxs = Vec::new();
for idx in matches {
if idx < pre_middlewares_len {
matched_pre_middleware_idxs.push(idx);
} else if idx >= pre_middlewares_len && idx < (pre_middlewares_len + routes_len) {
matched_route_idxs.push(idx - pre_middlewares_len);
} else if idx >= (pre_middlewares_len + routes_len)
&& idx < (pre_middlewares_len + routes_len + post_middlewares_len)
{
matched_post_middleware_idxs.push(idx - pre_middlewares_len - routes_len);
} else if idx >= (pre_middlewares_len + routes_len + post_middlewares_len)
&& idx < (pre_middlewares_len + routes_len + post_middlewares_len + scoped_data_maps_len)
{
matched_scoped_data_map_idxs.push(idx - pre_middlewares_len - routes_len - post_middlewares_len);
}
}
(
matched_pre_middleware_idxs,
matched_route_idxs,
matched_post_middleware_idxs,
matched_scoped_data_map_idxs,
)
}
}
impl<B, E> Debug for Router<B, E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{{ Pre-Middlewares: {:?}, Routes: {:?}, Post-Middlewares: {:?}, ScopedDataMaps: {:?}, ErrHandler: {:?}, ShouldGenReqInfo: {:?} }}",
self.pre_middlewares,
self.routes,
self.post_middlewares,
self.scoped_data_maps,
self.err_handler.is_some(),
self.should_gen_req_info
)
}
}