use core::{convert::Infallible, future::Future, marker::PhantomData};
use std::net::SocketAddr;
use xitca_service::{pipeline::PipelineE, AsyncClosure, Service};
use crate::http::{BorrowReq, Extensions, HeaderMap, Method, Request, RequestExt, Uri};
pub fn handler_service<F, T>(func: F) -> HandlerService<F, T, marker::BuilderMark>
where
F: AsyncClosure<T> + Clone,
{
HandlerService::new(func)
}
pub struct HandlerService<F, T, M> {
func: F,
_p: PhantomData<fn(T, M)>,
}
mod marker {
pub struct BuilderMark;
pub struct ServiceMark;
}
impl<F, T, M> HandlerService<F, T, M> {
pub const fn new(func: F) -> Self {
Self { func, _p: PhantomData }
}
}
impl<F, T, M> Clone for HandlerService<F, T, M>
where
F: Clone,
{
fn clone(&self) -> Self {
Self::new(self.func.clone())
}
}
impl<F, T> Service for HandlerService<F, T, marker::BuilderMark>
where
F: Clone,
{
type Response = HandlerService<F, T, marker::ServiceMark>;
type Error = Infallible;
async fn call(&self, _: ()) -> Result<Self::Response, Self::Error> {
Ok(HandlerService::new(self.func.clone()))
}
}
impl<F, Req, T, O> Service<Req> for HandlerService<F, T, marker::ServiceMark>
where
T: FromRequest<'static, Req>,
F: AsyncClosure<T>,
F: for<'a> AsyncClosure<T::Type<'a>, Output = O>,
O: Responder<Req>,
T::Error: From<O::Error>,
{
type Response = O::Response;
type Error = T::Error;
#[inline]
async fn call(&self, req: Req) -> Result<Self::Response, Self::Error> {
let extract = T::Type::<'_>::from_request(&req).await?;
let res = self.func.call(extract).await;
res.respond(req).await.map_err(Into::into)
}
}
pub trait FromRequest<'a, Req>: Sized {
type Type<'b>: FromRequest<'b, Req, Error = Self::Error>;
type Error;
fn from_request(req: &'a Req) -> impl Future<Output = Result<Self, Self::Error>>;
}
macro_rules! from_req_impl {
($req0: ident, $($req: ident,)*) => {
impl<'a, Req, $req0, $($req,)*> FromRequest<'a, Req> for ($req0, $($req,)*)
where
$req0: FromRequest<'a, Req>,
$(
$req: FromRequest<'a, Req>,
$req0::Error: From<$req::Error>,
)*
{
type Type<'r> = ($req0::Type<'r>, $($req::Type<'r>,)*);
type Error = $req0::Error;
#[inline]
async fn from_request(req: &'a Req) -> Result<Self, Self::Error> {
Ok((
$req0::from_request(req).await?,
$($req::from_request(req).await?,)*
))
}
}
}
}
from_req_impl! { A, }
from_req_impl! { A, B, }
from_req_impl! { A, B, C, }
from_req_impl! { A, B, C, D, }
from_req_impl! { A, B, C, D, E, }
from_req_impl! { A, B, C, D, E, F, }
from_req_impl! { A, B, C, D, E, F, G, }
from_req_impl! { A, B, C, D, E, F, G, H, }
from_req_impl! { A, B, C, D, E, F, G, H, I, }
pub trait Responder<Req> {
type Response;
type Error;
fn respond(self, req: Req) -> impl Future<Output = Result<Self::Response, Self::Error>>;
fn map(self, res: Self::Response) -> Result<Self::Response, Self::Error>
where
Self: Sized,
{
Ok(res)
}
}
macro_rules! responder_impl {
($res0: ident, $($res: ident,)*) => {
#[allow(non_snake_case)]
impl<Req, $res0, $($res,)*> Responder<Req> for ($res0, $($res,)*)
where
$res0: Responder<Req>,
$(
$res: Responder<Req, Response = $res0::Response>,
$res0::Error: From<$res::Error>,
)*
{
type Response = $res0::Response;
type Error = $res0::Error;
async fn respond(self, req: Req) -> Result<Self::Response, Self::Error> {
let ($res0, $($res,)*) = self;
let res = $res0.respond(req).await?;
$(
let res = $res.map(res)?;
)*
Ok(res)
}
fn map(self, mut res: Self::Response) -> Result<Self::Response, Self::Error> {
let ($res0, $($res,)*) = self;
res = $res0.map(res)?;
$(
res = $res.map(res)?;
)*
Ok(res)
}
}
}
}
responder_impl! { A, }
responder_impl! { A, B, }
responder_impl! { A, B, C, }
responder_impl! { A, B, C, D, }
responder_impl! { A, B, C, D, E, }
responder_impl! { A, B, C, D, E, F, }
impl<R, F, S> Responder<R> for PipelineE<F, S>
where
F: Responder<R>,
S: Responder<R, Response = F::Response>,
F::Error: From<S::Error>,
{
type Response = F::Response;
type Error = F::Error;
#[inline]
async fn respond(self, req: R) -> Result<Self::Response, Self::Error> {
match self {
Self::First(f) => f.respond(req).await,
Self::Second(s) => s.respond(req).await.map_err(From::from),
}
}
}
macro_rules! borrow_req_impl {
($tt: tt) => {
impl<'a, Ext> FromRequest<'a, Request<Ext>> for &'a $tt {
type Type<'b> = &'b $tt;
type Error = Infallible;
#[inline]
async fn from_request(req: &'a Request<Ext>) -> Result<Self, Self::Error> {
Ok(req.borrow())
}
}
};
}
borrow_req_impl!(Method);
borrow_req_impl!(Uri);
borrow_req_impl!(HeaderMap);
borrow_req_impl!(Extensions);
impl<'a, Ext> FromRequest<'a, Request<Ext>> for &'a Request<Ext>
where
Ext: 'static,
{
type Type<'b> = &'b Request<Ext>;
type Error = Infallible;
#[inline]
async fn from_request(req: &'a Request<Ext>) -> Result<Self, Self::Error> {
Ok(req)
}
}
impl<'a, B> FromRequest<'a, Request<RequestExt<B>>> for &'a SocketAddr {
type Type<'b> = &'b SocketAddr;
type Error = Infallible;
#[inline]
async fn from_request(req: &'a Request<RequestExt<B>>) -> Result<Self, Self::Error> {
Ok(req.borrow())
}
}
#[cfg(test)]
mod test {
use xitca_service::ServiceExt;
use xitca_unsafe_collection::futures::NowOrPanic;
use crate::{
http::{Response, StatusCode},
unspecified_socket_addr,
};
use super::*;
async fn handler(
method: &Method,
addr: &SocketAddr,
uri: &Uri,
headers: &HeaderMap,
(_, ext): (&Request<RequestExt<()>>, &Extensions),
) -> StatusCode {
assert_eq!(method, Method::GET);
assert_eq!(*addr, unspecified_socket_addr());
assert_eq!(uri.path(), "/");
assert!(headers.is_empty());
assert!(ext.is_empty());
StatusCode::MULTI_STATUS
}
impl Responder<Request<RequestExt<()>>> for StatusCode {
type Response = Response<()>;
type Error = Infallible;
async fn respond(self, _: Request<RequestExt<()>>) -> Result<Self::Response, Self::Error> {
let mut res = Response::new(());
*res.status_mut() = self;
Ok(res)
}
}
#[test]
fn concurrent_extract_with_enclosed_fn() {
async fn enclosed<S, Req>(service: &S, req: Req) -> Result<S::Response, S::Error>
where
S: Service<Req>,
{
service.call(req).await
}
let res = handler_service(handler)
.enclosed_fn(enclosed)
.call(())
.now_or_panic()
.unwrap()
.call(Request::default())
.now_or_panic()
.unwrap();
assert_eq!(res.status(), StatusCode::MULTI_STATUS);
}
#[cfg(feature = "router")]
#[test]
fn handler_in_router() {
use crate::util::service::{route::get, Router};
let res = Router::new()
.insert("/", get(handler_service(handler)))
.call(())
.now_or_panic()
.unwrap()
.call(Request::default())
.now_or_panic()
.unwrap();
assert_eq!(res.status(), StatusCode::MULTI_STATUS);
}
}