pub mod nested;
pub mod router_svc;
use crate::AppError;
#[cfg(feature = "ext")]
use crate::ext::static_svc::StaticSvcBuilder;
use crate::extractor::{from_request::FromRequest, path_params::PathParams};
use crate::handler::{DynHandler, handler_to_svc};
use crate::handler::{FnOnceTuple, Req, Resp, TypedHandler};
use crate::http::response::into_response::IntoResponse;
use crate::router::router_svc::RouterSvc;
use bytes::Bytes;
use http_body_util::{BodyExt, Full};
use hyper::{Method, Request, Response, body::Incoming};
use matchit::Router as MRouter;
use miko_core::{BoxError, IntoMethods, MikoError, encode_route};
use nested::NestLayer;
#[cfg(feature = "ext")]
use std::path::PathBuf;
use std::{collections::HashMap, sync::Arc};
use tower::{Layer, Service, util::BoxCloneService};
macro_rules! define_method {
($name:ident, $m:ident) => {
pub fn $name<F, A, Fut, R, M>(&mut self, path: &str, handler: F) -> &mut Self
where
F: FnOnceTuple<A, Output = Fut> + Clone + Send + Sync + 'static,
A: FromRequest<S, M> + Send + 'static,
Fut: Future<Output = R> + Send + 'static,
R: IntoResponse,
M: Send + Sync + 'static,
{
let handler = Arc::new(TypedHandler::new(handler, self.state.clone())) as DynHandler;
self.routes
.entry(Method::$m)
.or_default()
.insert(encode_route(path), handler_to_svc(handler.clone()))
.unwrap();
self.path_map
.entry(Method::$m)
.or_default()
.insert(path.to_string(), handler_to_svc(handler.clone()));
self
}
};
}
macro_rules! define_handle_service {
($name:ident, $m:ident) => {
pub fn $name(&mut self, path: &str, svc: HttpSvc<Req>) -> &mut Self {
self.routes
.entry(Method::$m.clone())
.or_insert_with(|| MRouter::new())
.insert(encode_route(path), svc.clone())
.unwrap();
self.path_map
.entry(Method::$m.clone())
.or_insert_with(|| HashMap::new())
.insert(path.to_string(), svc.clone());
self
}
};
}
pub type HttpReq = Request<Incoming>;
pub type HttpSvc<T = HttpReq> = BoxCloneService<T, Resp, AppError>;
type MikoLayer<T = Req> = Arc<dyn Fn(HttpSvc<T>) -> HttpSvc<T> + Send + Sync>;
pub struct Router<S = ()> {
pub routes: HashMap<Method, MRouter<HttpSvc<Req>>>,
pub state: Arc<S>,
pub layers: Vec<MikoLayer>,
pub path_map: HashMap<Method, HashMap<String, HttpSvc<Req>>>,
}
impl<S> Clone for Router<S> {
fn clone(&self) -> Self {
Self {
routes: self.routes.clone(),
state: self.state.clone(),
layers: self.layers.clone(),
path_map: self.path_map.clone(),
}
}
}
impl<S: Send + Sync + 'static> Router<S> {
pub fn find_handler(&self, method: &Method, path: &str) -> Option<(HttpSvc<Req>, PathParams)> {
if let Some(router) = self.routes.get(method) {
match router.at(path) {
Ok(matched) => {
let handler = matched.value.clone();
Some((handler, PathParams::from(&matched.params)))
}
Err(_e) => None,
}
} else {
None
}
}
pub async fn handle(&self, method: &Method, path: &str, mut req: Req) -> Resp {
if let Some(router) = self.routes.get(method) {
match router.at(path) {
Ok(matched) => {
req.extensions_mut()
.insert(PathParams::from(&matched.params));
let mut handler = matched.value.clone();
handler.call(req).await.into_response()
}
Err(_e) => Response::builder()
.status(hyper::StatusCode::NOT_FOUND)
.body(
Full::new(Bytes::from("Not Found"))
.map_err(Into::into)
.boxed_unsync(),
)
.unwrap(),
}
} else {
Response::builder()
.status(hyper::StatusCode::NOT_FOUND)
.body(
Full::new(Bytes::from("Not Found"))
.map_err(Into::into)
.boxed_unsync(),
)
.unwrap()
}
}
}
impl Router {
pub fn new() -> Self {
Self {
routes: HashMap::new(),
state: Arc::new(()),
layers: Vec::new(),
path_map: HashMap::new(),
}
}
}
impl<S: Send + Sync + 'static> Router<S> {
pub fn route<F, A, Fut, R, M>(
&mut self,
method: impl IntoMethods,
path: &str,
handler: F,
) -> &mut Self
where
F: FnOnceTuple<A, Output = Fut> + Clone + Send + Sync + 'static,
A: FromRequest<S, M> + Send + 'static,
Fut: Future<Output = R> + Send + 'static,
R: IntoResponse,
M: Send + Sync + 'static,
{
let handler = Arc::new(TypedHandler::new(handler, self.state.clone())) as DynHandler;
for m in method.into_methods() {
self.routes
.entry(m.clone())
.or_default()
.insert(encode_route(path), handler_to_svc(handler.clone()))
.unwrap();
self.path_map
.entry(m.clone())
.or_default()
.insert(path.to_string(), handler_to_svc(handler.clone()));
}
self
}
define_method!(get, GET);
define_method!(post, POST);
define_method!(put, PUT);
define_method!(delete, DELETE);
define_method!(head, HEAD);
define_method!(options, OPTIONS);
define_method!(trace, TRACE);
define_method!(connect, CONNECT);
define_method!(patch, PATCH);
define_handle_service!(get_service, GET);
define_handle_service!(post_service, POST);
define_handle_service!(put_service, PUT);
define_handle_service!(delete_service, DELETE);
define_handle_service!(head_service, HEAD);
define_handle_service!(options_service, OPTIONS);
define_handle_service!(trace_service, TRACE);
define_handle_service!(connect_service, CONNECT);
define_handle_service!(patch_service, PATCH);
}
impl<S: Send + Sync + 'static> Router<S> {
pub fn with_state<T>(self, state: T) -> Router<T> {
Router {
routes: self.routes,
state: Arc::new(state),
layers: self.layers,
path_map: self.path_map,
}
}
pub fn merge<T>(&mut self, mut other: Router<T>) -> &mut Self {
let layers = std::mem::take(&mut other.layers);
for (method, _) in other.routes.drain() {
for (path, mut svc) in other.path_map.get_mut(&method).unwrap().drain() {
for apply in &layers {
svc = apply(svc);
}
let boxed: HttpSvc<Req> = BoxCloneService::new(svc);
self.routes
.entry(method.clone())
.or_default()
.insert(&path, boxed.clone())
.unwrap();
self.path_map
.entry(method.clone())
.or_default()
.insert(path, boxed.clone());
}
}
self
}
pub fn nest<T>(&mut self, prefix: &str, mut other: Router<T>) -> &mut Self {
let prefix = prefix.trim_end_matches('/').to_string();
let layers = std::mem::take(&mut other.layers);
for (method, _) in other.routes.drain() {
for (path, mut svc) in other.path_map.get_mut(&method).unwrap().drain() {
for apply in &layers {
svc = apply(svc);
}
let layered = NestLayer::new(&prefix).layer(svc);
let boxed: HttpSvc<Req> = BoxCloneService::new(layered);
let new_path = format!("{}{}", prefix, path);
self.routes
.entry(method.clone())
.or_default()
.insert(&new_path, boxed.clone())
.unwrap();
self.path_map
.entry(method.clone())
.or_default()
.insert(new_path, boxed.clone());
}
}
self
}
pub fn nest_service(&mut self, prefix: &str, svc: HttpSvc<Req>) {
let prefix = prefix.trim_end_matches('/').to_string();
let layered = NestLayer::new(&prefix).layer(svc);
let boxed: HttpSvc<Req> = BoxCloneService::new(layered);
let methods = [
Method::GET,
Method::POST,
Method::PUT,
Method::DELETE,
Method::PATCH,
Method::HEAD,
Method::OPTIONS,
];
let new_path = format!("{}{}", prefix, "/{*rest}");
let new_path_index = format!("{}{}", prefix, "/");
for method in methods {
self.routes
.entry(method.clone())
.or_default()
.insert(&new_path, boxed.clone())
.unwrap();
self.path_map
.entry(method.clone())
.or_default()
.insert(new_path.clone(), boxed.clone());
self.routes
.entry(method.clone())
.or_default()
.insert(&new_path_index, boxed.clone())
.unwrap();
self.path_map
.entry(method.clone())
.or_default()
.insert(new_path_index.clone(), boxed.clone());
}
}
pub fn service(&mut self, path: &str, svc: HttpSvc<Req>) {
let methods = [
Method::GET,
Method::POST,
Method::PUT,
Method::DELETE,
Method::PATCH,
Method::HEAD,
Method::OPTIONS,
];
for method in methods {
self.routes
.entry(method.clone())
.or_default()
.insert(encode_route(path), svc.clone())
.unwrap();
self.path_map
.entry(method.clone())
.or_default()
.insert(path.to_string(), svc.clone());
}
}
pub fn with_layer<L, B>(&mut self, layer: L) -> &mut Self
where
L: Layer<HttpSvc<Req>> + Send + Sync + 'static,
L::Service: Service<Req, Response = Response<B>> + Clone + Send + 'static,
<L::Service as Service<Req>>::Error: Into<AppError> + Send + Sync + 'static,
<L::Service as Service<Req>>::Future: Send + 'static,
B: http_body::Body<Data = Bytes> + Send + 'static,
B::Error: Into<BoxError>,
{
self.layers.push(Arc::new(move |svc: HttpSvc<Req>| {
let wrapped = layer.layer(svc);
let standardized = tower::ServiceBuilder::new()
.map_response(|resp: Response<B>| {
let (parts, body) = resp.into_parts();
let body = body.map_err(|e| MikoError::from(e.into())).boxed_unsync();
Response::from_parts(parts, body)
})
.map_err(Into::into)
.service(wrapped);
BoxCloneService::new(standardized)
}));
self
}
#[cfg(feature = "catch_panic")]
pub fn with_catch_panic(&mut self) -> &mut Self {
use tower_http::catch_panic::CatchPanicLayer;
self.with_layer(CatchPanicLayer::custom(|payload| {
AppError::from_panic(payload).into_response()
}))
}
pub fn into_tower_service(self) -> HttpSvc<Req> {
self.into_tower_service_with_container(crate::dependency_container::default_container())
}
pub fn into_tower_service_with_container(
mut self,
container: Arc<crate::dependency_container::LazyDependencyContainer>,
) -> HttpSvc<Req> {
let layers = std::mem::take(&mut self.layers);
let router_svc = RouterSvc { router: self };
let mut svc: HttpSvc<Req> = BoxCloneService::new(router_svc);
for apply in layers {
svc = apply(svc);
}
crate::dependency_container::with_dependency_scope(svc, container)
}
pub fn take(&mut self) -> Self {
std::mem::replace(
self,
Router {
routes: HashMap::new(),
state: self.state.clone(),
layers: Vec::new(),
path_map: HashMap::new(),
},
)
}
}
#[cfg(feature = "test")]
impl<S: Send + Sync + 'static> Router<S> {
pub fn test_client(self) -> crate::test::test_client::TestClient {
crate::test::test_client::TestClient::new(self.into_tower_service())
}
}
#[cfg(feature = "ext")]
impl<S: Send + Sync + 'static> Router<S> {
pub fn static_svc<F>(
&mut self,
prefix: &str,
root: impl Into<PathBuf>,
option_closure: Option<F>,
) where
F: FnOnce(StaticSvcBuilder) -> StaticSvcBuilder,
{
let builder = StaticSvcBuilder::new(root);
let builder = if let Some(option_closure) = option_closure {
option_closure(builder)
} else {
builder
};
self.nest_service(prefix, builder.build())
}
pub fn cors_any(&mut self) {
use tower_http::cors::CorsLayer;
self.with_layer(CorsLayer::permissive());
}
}