Unverified Commit b2d280fc authored by 82marbag's avatar 82marbag Committed by GitHub
Browse files

Multiple FromParts in handlers (#1999)



* Multiple FromParts in handlers

Handlers can have up to 8 extensions:
```
pub async fn handler(
    input: input::Input,
    ext0: Extension<...>,
    ext1: Extension<...>,
    ext2: Extension<...>,
    ext3: Extension<...>,
    ext4: Extension<...>,
    ext5: Extension<...>,
    ext6: Extension<...>,
    ext7: Extension<...>,
) -> Result<output::Output, error::Error> { ... }
```

Signed-off-by: default avatarDaniele Ahmed <ahmeddan@amazon.de>
Co-authored-by: default avatarHarry Barber <hlbarber@amazon.co.uk>
parent ed5cd6e3
Loading
Loading
Loading
Loading
+28 −27
Original line number Diff line number Diff line
@@ -62,36 +62,37 @@ where
    }
}

// fn(Input, Ext0) -> Output
impl<Op, F, Fut, Ext0> Handler<Op, (Ext0,)> for F
// fn(Input, Ext_i) -> Output
macro_rules! impl_handler {
    ($($var:ident),+) => (
        impl<Op, F, Fut, $($var,)*> Handler<Op, ($($var,)*)> for F
        where
            Op: OperationShape,
    F: Fn(Op::Input, Ext0) -> Fut,
            F: Fn(Op::Input, $($var,)*) -> Fut,
            Fut: Future,
            Fut::Output: IntoResult<Op::Output, Op::Error>,
        {
            type Future = Map<Fut, fn(Fut::Output) -> Result<Op::Output, Op::Error>>;

    fn call(&mut self, input: Op::Input, exts: (Ext0,)) -> Self::Future {
        (self)(input, exts.0).map(IntoResult::into_result)
            fn call(&mut self, input: Op::Input, exts: ($($var,)*)) -> Self::Future {
                #[allow(non_snake_case)]
                let ($($var,)*) = exts;
                (self)(input, $($var,)*).map(IntoResult::into_result)
            }
        }

// fn(Input, Ext0, Ext1) -> Output
impl<Op, F, Fut, Ext0, Ext1> Handler<Op, (Ext0, Ext1)> for F
where
    Op: OperationShape,
    F: Fn(Op::Input, Ext0, Ext1) -> Fut,
    Fut: Future,
    Fut::Output: IntoResult<Op::Output, Op::Error>,
{
    type Future = Map<Fut, fn(Fut::Output) -> Result<Op::Output, Op::Error>>;

    fn call(&mut self, input: Op::Input, exts: (Ext0, Ext1)) -> Self::Future {
        (self)(input, exts.0, exts.1).map(IntoResult::into_result)
    }
    )
}

impl_handler!(Exts0);
impl_handler!(Exts0, Exts1);
impl_handler!(Exts0, Exts1, Exts2);
impl_handler!(Exts0, Exts1, Exts2, Exts3);
impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4);
impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5);
impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5, Exts6);
impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5, Exts6, Exts7);
impl_handler!(Exts0, Exts1, Exts2, Exts3, Exts4, Exts5, Exts6, Exts7, Exts8);

/// An extension trait for [`Handler`].
pub trait HandlerExt<Op, Exts>: Handler<Op, Exts>
where
+33 −16
Original line number Diff line number Diff line
@@ -264,21 +264,38 @@ convert_to_request_rejection!(hyper::Error, HttpBody);
// Required in order to accept Lambda HTTP requests using `Router<lambda_http::Body>`.
convert_to_request_rejection!(lambda_http::Error, HttpBody);

/// A sum type rejection, implementing [`IntoResponse`] when both variants do.
pub enum EitherRejection<Left, Right> {
    Left(Left),
    Right(Right),
pub mod any_rejections {
    //! This module hosts enums, up to size 8, which implement [`IntoResponse`] when their variants implement
    //! [`IntoResponse`].

    use super::IntoResponse;

    macro_rules! any_rejection {
        ($name:ident, $($var:ident),+) => (
            pub enum $name<$($var),*> {
                $($var ($var),)*
            }

impl<P, L, R> IntoResponse<P> for EitherRejection<L, R>
            impl<P, $($var,)*> IntoResponse<P> for $name<$($var),*>
            where
    L: IntoResponse<P>,
    R: IntoResponse<P>,
                $($var: IntoResponse<P>,)*
            {
                #[allow(non_snake_case)]
                fn into_response(self) -> http::Response<crate::body::BoxBody> {
                    match self {
            EitherRejection::Left(left) => left.into_response(),
            EitherRejection::Right(right) => right.into_response(),
                        $($name::$var ($var) => $var.into_response(),)*
                    }
                }
            }
        )
    }

    // any_rejection!(One, A);
    any_rejection!(Two, A, B);
    any_rejection!(Three, A, B, C);
    any_rejection!(Four, A, B, C, D);
    any_rejection!(Five, A, B, C, D, E);
    any_rejection!(Six, A, B, C, D, E, F);
    any_rejection!(Seven, A, B, C, D, E, F, G);
    any_rejection!(Eight, A, B, C, D, E, F, G, H);
}
+28 −16
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ use futures_util::{
};
use http::{request::Parts, Extensions, HeaderMap, Request, Uri};

use crate::{rejection::EitherRejection, response::IntoResponse};
use crate::{rejection::any_rejections, response::IntoResponse};

#[doc(hidden)]
#[derive(Debug)]
@@ -139,20 +139,32 @@ where
    }
}

impl<P, T1, T2> FromParts<P> for (T1, T2)
macro_rules! impl_from_parts {
    ($error_name:ident, $($var:ident),+) => (
        impl<P, $($var,)*> FromParts<P> for ($($var),*)
        where
    T1: FromParts<P>,
    T2: FromParts<P>,
            $($var: FromParts<P>,)*
        {
    type Rejection = EitherRejection<T1::Rejection, T2::Rejection>;
            type Rejection = any_rejections::$error_name<$($var::Rejection),*>;

            fn from_parts(parts: &mut Parts) -> Result<Self, Self::Rejection> {
        let t1 = T1::from_parts(parts).map_err(EitherRejection::Left)?;
        let t2 = T2::from_parts(parts).map_err(EitherRejection::Right)?;
        Ok((t1, t2))
                let tuple = (
                    $($var::from_parts(parts).map_err(any_rejections::$error_name::$var)?,)*
                );
                Ok(tuple)
            }
        }
    )
}

impl_from_parts!(Two, A, B);
impl_from_parts!(Three, A, B, C);
impl_from_parts!(Four, A, B, C, D);
impl_from_parts!(Five, A, B, C, D, E);
impl_from_parts!(Six, A, B, C, D, E, F);
impl_from_parts!(Seven, A, B, C, D, E, F, G);
impl_from_parts!(Eight, A, B, C, D, E, F, G, H);

/// Provides a protocol aware extraction from a [`Request`]. This consumes the
/// [`Request`], in contrast to [`FromParts`].
pub trait FromRequest<Protocol, B>: Sized {
@@ -180,14 +192,14 @@ where
    T1: FromRequest<P, B>,
    T2: FromParts<P>,
{
    type Rejection = EitherRejection<T1::Rejection, T2::Rejection>;
    type Rejection = any_rejections::Two<T1::Rejection, T2::Rejection>;
    type Future = TryJoin<MapErr<T1::Future, fn(T1::Rejection) -> Self::Rejection>, Ready<Result<T2, Self::Rejection>>>;

    fn from_request(request: Request<B>) -> Self::Future {
        let (mut parts, body) = request.into_parts();
        let t2_result = T2::from_parts(&mut parts).map_err(EitherRejection::Right);
        let t2_result = T2::from_parts(&mut parts).map_err(any_rejections::Two::B);
        try_join(
            T1::from_request(Request::from_parts(parts, body)).map_err(EitherRejection::Left),
            T1::from_request(Request::from_parts(parts, body)).map_err(any_rejections::Two::A),
            ready(t2_result),
        )
    }