use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use typeway_core::ApiSpec;
use crate::body::BoxBody;
use crate::extract::{FromRequest, FromRequestParts};
use crate::handler::BoxedHandler;
use crate::handler_for::{BindableEndpoint, BoundHandler};
use crate::response::IntoResponse;
pub struct Strict<E> {
_marker: PhantomData<E>,
}
impl<E: ApiSpec> ApiSpec for Strict<E> {}
pub trait HasResType {
type Res;
}
impl<M: typeway_core::HttpMethod, P: typeway_core::PathSpec, Req, Res, Q, Err> HasResType
for typeway_core::Endpoint<M, P, Req, Res, Q, Err>
{
type Res = Res;
}
pub trait HasErrType {
type Err;
}
impl<M: typeway_core::HttpMethod, P: typeway_core::PathSpec, Req, Res, Q, Err> HasErrType
for typeway_core::Endpoint<M, P, Req, Res, Q, Err>
{
type Err = Err;
}
pub trait StrictHandler<Res, Args>: Clone + Send + Sync + 'static {
fn call(
self,
parts: http::request::Parts,
body: bytes::Bytes,
) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>>;
}
impl<F, Fut, Res> StrictHandler<Res, ()> for F
where
F: FnOnce() -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
{
fn call(
self,
_parts: http::request::Parts,
_body: bytes::Bytes,
) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
Box::pin(async move { self().await.into_response() })
}
}
macro_rules! impl_strict_handler {
([$($T:ident),+], [$($t:ident),+]) => {
#[allow(non_snake_case)]
impl<F, Fut, Res, $($T,)+> StrictHandler<Res, ($($T,)+)> for F
where
F: FnOnce($($T,)+) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
$($T: FromRequestParts + 'static,)+
{
fn call(
self,
parts: http::request::Parts,
_body: bytes::Bytes,
) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
Box::pin(async move {
$(
let $t = match $T::from_request_parts(&parts) {
Ok(v) => v,
Err(e) => return e.into_response(),
};
)+
self($($t,)+).await.into_response()
})
}
}
};
}
impl_strict_handler!([T1], [t1]);
impl_strict_handler!([T1, T2], [t1, t2]);
impl_strict_handler!([T1, T2, T3], [t1, t2, t3]);
impl_strict_handler!([T1, T2, T3, T4], [t1, t2, t3, t4]);
impl_strict_handler!([T1, T2, T3, T4, T5], [t1, t2, t3, t4, t5]);
impl_strict_handler!([T1, T2, T3, T4, T5, T6], [t1, t2, t3, t4, t5, t6]);
pub struct StrictWithBody<Parts, Body>(PhantomData<(Parts, Body)>);
impl<F, Fut, Res, B> StrictHandler<Res, StrictWithBody<(), B>> for F
where
F: FnOnce(B) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
B: FromRequest + 'static,
{
fn call(
self,
parts: http::request::Parts,
body: bytes::Bytes,
) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
Box::pin(async move {
let b = match B::from_request(&parts, body).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
self(b).await.into_response()
})
}
}
macro_rules! impl_strict_handler_body_marker {
([$($T:ident),+], [$($t:ident),+]) => {
#[allow(non_snake_case)]
impl<F, Fut, Res, $($T,)+ B> StrictHandler<Res, StrictWithBody<($($T,)+), B>> for F
where
F: FnOnce($($T,)+ B) -> Fut + Clone + Send + Sync + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
$($T: FromRequestParts + 'static,)+
B: FromRequest + 'static,
{
fn call(
self,
parts: http::request::Parts,
body: bytes::Bytes,
) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
Box::pin(async move {
$(
let $t = match $T::from_request_parts(&parts) {
Ok(v) => v,
Err(e) => return e.into_response(),
};
)+
let b = match B::from_request(&parts, body).await {
Ok(v) => v,
Err(e) => return e.into_response(),
};
self($($t,)+ b).await.into_response()
})
}
}
};
}
impl_strict_handler_body_marker!([T1], [t1]);
impl_strict_handler_body_marker!([T1, T2], [t1, t2]);
impl_strict_handler_body_marker!([T1, T2, T3], [t1, t2, t3]);
impl_strict_handler_body_marker!([T1, T2, T3, T4], [t1, t2, t3, t4]);
impl_strict_handler_body_marker!([T1, T2, T3, T4, T5], [t1, t2, t3, t4, t5]);
pub trait StrictEndpoint {
type Inner: BindableEndpoint + HasResType;
}
impl<E: BindableEndpoint + HasResType> StrictEndpoint for Strict<E> {
type Inner = E;
}
pub fn bind_strict<S, H, Args>(handler: H) -> BoundHandler<S>
where
S: StrictEndpoint,
H: StrictHandler<<S::Inner as HasResType>::Res, Args>,
Args: 'static,
{
let method = S::Inner::method();
let pattern = S::Inner::pattern();
let match_fn = S::Inner::match_fn();
let boxed: BoxedHandler = std::sync::Arc::new(move |parts, body| {
let h = handler.clone();
h.call(parts, body)
});
BoundHandler::new(method, pattern, match_fn, boxed)
}
#[macro_export]
macro_rules! bind_strict {
($handler:expr) => {
$crate::typed_response::bind_strict::<_, _, _>($handler)
};
}