fnroute 0.1.0

A small function router with axum-style handler extraction.
Documentation
use alloc::boxed::Box;
use core::future::Future;

use crate::{BoxFuture, FromRoute, RouteContext, RouteResult};

pub trait Handler<T, S, R>: Clone + 'static {
    fn call(self, context: RouteContext, state: S) -> BoxFuture<RouteResult<R>>;
}

pub trait SyncHandler<T, S, R>: Clone + 'static {
    fn call_sync(self, context: RouteContext, state: S) -> RouteResult<R>;
}

impl<Func, Fut, Out, S, R> Handler<(), S, R> for Func
where
    Func: FnOnce() -> Fut + Clone + 'static,
    Fut: Future<Output = Out> + 'static,
    Out: Into<R>,
    S: 'static,
    R: 'static,
{
    fn call(self, _context: RouteContext, _state: S) -> BoxFuture<RouteResult<R>> {
        let future = self();
        Box::pin(async move { Ok(future.await.into()) })
    }
}

impl<Func, Out, S, R> SyncHandler<(), S, R> for Func
where
    Func: FnOnce() -> Out + Clone + 'static,
    Out: Into<R>,
    S: 'static,
    R: 'static,
{
    fn call_sync(self, _context: RouteContext, _state: S) -> RouteResult<R> {
        Ok(self().into())
    }
}

macro_rules! impl_handler {
    ($($ty:ident),+) => {
        impl<Func, Fut, Out, S, R, $($ty,)+> Handler<($($ty,)+), S, R> for Func
        where
            Func: FnOnce($($ty),+) -> Fut + Clone + 'static,
            Fut: Future<Output = Out> + 'static,
            Out: Into<R>,
            S: Clone + 'static,
            R: 'static,
            $($ty: FromRoute<S>,)+
        {
            #[allow(non_snake_case)]
            fn call(self, context: RouteContext, state: S) -> BoxFuture<RouteResult<R>> {
                Box::pin(async move {
                    $(
                        let $ty = $ty::from_route(&context, &state)?;
                    )+

                    Ok(self($($ty),+).await.into())
                })
            }
        }
    };
}

macro_rules! impl_sync_handler {
    ($($ty:ident),+) => {
        impl<Func, Out, S, R, $($ty,)+> SyncHandler<($($ty,)+), S, R> for Func
        where
            Func: FnOnce($($ty),+) -> Out + Clone + 'static,
            Out: Into<R>,
            S: Clone + 'static,
            R: 'static,
            $($ty: FromRoute<S>,)+
        {
            #[allow(non_snake_case)]
            fn call_sync(self, context: RouteContext, state: S) -> RouteResult<R> {
                $(
                    let $ty = $ty::from_route(&context, &state)?;
                )+

                Ok(self($($ty),+).into())
            }
        }
    };
}

impl_handler!(A);
impl_handler!(A, B);
impl_handler!(A, B, C);
impl_handler!(A, B, C, D);
impl_handler!(A, B, C, D, E);
impl_handler!(A, B, C, D, E, F);
impl_handler!(A, B, C, D, E, F, G);
impl_handler!(A, B, C, D, E, F, G, H);
impl_handler!(A, B, C, D, E, F, G, H, I);
impl_handler!(A, B, C, D, E, F, G, H, I, J);
impl_handler!(A, B, C, D, E, F, G, H, I, J, K);
impl_handler!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);

impl_sync_handler!(A);
impl_sync_handler!(A, B);
impl_sync_handler!(A, B, C);
impl_sync_handler!(A, B, C, D);
impl_sync_handler!(A, B, C, D, E);
impl_sync_handler!(A, B, C, D, E, F);
impl_sync_handler!(A, B, C, D, E, F, G);
impl_sync_handler!(A, B, C, D, E, F, G, H);
impl_sync_handler!(A, B, C, D, E, F, G, H, I);
impl_sync_handler!(A, B, C, D, E, F, G, H, I, J);
impl_sync_handler!(A, B, C, D, E, F, G, H, I, J, K);
impl_sync_handler!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_sync_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_sync_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_sync_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_sync_handler!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);