use crate::error::Error;
use crate::http::{IntoResponse, endpoints::args::FromRequest};
use crate::{HttpRequest, HttpResult};
use futures_util::future::BoxFuture;
use std::{future::Future, sync::Arc};
pub(crate) type RouteHandler = Arc<dyn Handler + Send + Sync>;
pub(crate) trait Handler {
fn call(&self, req: HttpRequest) -> BoxFuture<'_, HttpResult>;
}
pub(crate) struct Func<F, R, Args>
where
F: GenericHandler<Args, Output = R>,
R: IntoResponse,
Args: FromRequest,
{
func: F,
_marker: std::marker::PhantomData<fn(Args)>,
}
impl<F, R, Args> Func<F, R, Args>
where
F: GenericHandler<Args, Output = R>,
R: IntoResponse,
Args: FromRequest,
{
#[inline]
pub(crate) fn new(func: F) -> Arc<Self> {
Arc::new(Self::new_local(func))
}
#[inline]
pub(crate) fn new_local(func: F) -> Self {
Self {
func,
_marker: std::marker::PhantomData,
}
}
}
impl<F, R, Args> Handler for Func<F, R, Args>
where
F: GenericHandler<Args, Output = R>,
R: IntoResponse,
Args: FromRequest + Send,
{
#[inline]
fn call(&self, req: HttpRequest) -> BoxFuture<'_, HttpResult> {
Box::pin(async move {
let args = Args::from_request(req).await?;
self.func.call(args).await.into_response()
})
}
}
pub trait GenericHandler<Args>: Clone + Send + Sync + 'static {
type Output;
fn call(&self, args: Args) -> impl Future<Output = Self::Output> + Send;
}
pub trait MapErr<Args>: Clone + Send + Sync + 'static {
type Output;
fn map_err(&self, err: Error, args: Args) -> impl Future<Output = Self::Output> + Send;
}
macro_rules! define_generic_handler ({ $($param:ident)* } => {
impl<Func, Fut: Send, $($param,)*> GenericHandler<($($param,)*)> for Func
where
Func: Fn($($param),*) -> Fut + Send + Sync + Clone + 'static,
Fut: Future,
{
type Output = Fut::Output;
#[inline]
#[allow(non_snake_case)]
fn call(&self, ($($param,)*): ($($param,)*)) -> impl Future<Output = Self::Output> + Send {
(self)($($param,)*)
}
}
impl<Func, Fut: Send, $($param,)*> MapErr<($($param,)*)> for Func
where
Func: Fn(Error, $($param,)*) -> Fut + Send + Sync + Clone + 'static,
Fut: Future,
{
type Output = Fut::Output;
#[inline]
#[allow(non_snake_case)]
fn map_err(&self, err: Error, ($($param,)*): ($($param,)*)) -> impl Future<Output = Self::Output> {
(self)(err, $($param,)*)
}
}
});
define_generic_handler! {}
define_generic_handler! { T1 }
define_generic_handler! { T1 T2 }
define_generic_handler! { T1 T2 T3 }
define_generic_handler! { T1 T2 T3 T4 }
define_generic_handler! { T1 T2 T3 T4 T5 }
define_generic_handler! { T1 T2 T3 T4 T5 T6 }
define_generic_handler! { T1 T2 T3 T4 T5 T6 T7 }
define_generic_handler! { T1 T2 T3 T4 T5 T6 T7 T8 }
define_generic_handler! { T1 T2 T3 T4 T5 T6 T7 T8 T9 }
define_generic_handler! { T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 }
#[cfg(test)]
mod tests {
use super::{GenericHandler, MapErr};
use crate::error::Error;
#[tokio::test]
async fn generic_handler_invokes_function_with_arguments() {
let handler = |a: i32, b: i32| async move { a + b };
let result = GenericHandler::call(&handler, (2, 3)).await;
assert_eq!(result, 5);
}
#[tokio::test]
async fn map_err_handler_invokes_function_with_error_and_args() {
let handler = |err: Error, code: u16| async move { (err.status.as_u16(), code) };
let err = Error::client_error("bad");
let result = MapErr::map_err(&handler, err, (42,)).await;
assert_eq!(result, (400, 42));
}
}