use fnv::FnvHashMap;
use futures::future::FutureObj;
use http_service::Body;
use route_recognizer::{Match, Params, Router as MethodRouter};
use crate::{
endpoint::{DynEndpoint, Endpoint},
Context, Response,
};
pub(crate) struct Router<AppData> {
method_map: FnvHashMap<http::Method, MethodRouter<Box<DynEndpoint<AppData>>>>,
}
pub(crate) struct Selection<'a, AppData> {
pub(crate) endpoint: &'a DynEndpoint<AppData>,
pub(crate) params: Params,
}
impl<AppData: 'static> Router<AppData> {
pub(crate) fn new() -> Router<AppData> {
Router {
method_map: FnvHashMap::default(),
}
}
pub(crate) fn add(&mut self, path: &str, method: http::Method, ep: impl Endpoint<AppData>) {
self.method_map
.entry(method)
.or_insert_with(MethodRouter::new)
.add(
path,
Box::new(move |cx| FutureObj::new(Box::new(ep.call(cx)))),
)
}
pub(crate) fn route(&self, path: &str, method: http::Method) -> Selection<'_, AppData> {
if let Some(Match { handler, params }) = self
.method_map
.get(&method)
.and_then(|r| r.recognize(path).ok())
{
Selection {
endpoint: &**handler,
params,
}
} else if method == http::Method::HEAD {
self.route(path, http::Method::GET)
} else {
Selection {
endpoint: ¬_found_endpoint,
params: Params::new(),
}
}
}
}
fn not_found_endpoint<Data>(_cx: Context<Data>) -> FutureObj<'static, Response> {
box_async! {
http::Response::builder().status(http::StatusCode::NOT_FOUND).body(Body::empty()).unwrap()
}
}