blueprint_core/extract/
tuple.rs

1use super::{FromJobCall, FromJobCallParts, JobCall};
2use crate::JobResult;
3use crate::job::call::Parts;
4use crate::job::result::IntoJobResult;
5use core::convert::Infallible;
6
7impl<Ctx> FromJobCallParts<Ctx> for ()
8where
9    Ctx: Send + Sync,
10{
11    type Rejection = Infallible;
12
13    async fn from_job_call_parts(_: &mut Parts, _: &Ctx) -> Result<(), Self::Rejection> {
14        Ok(())
15    }
16}
17
18macro_rules! impl_from_job_call {
19    (
20        [$($ty:ident),*], $last:ident
21    ) => {
22        #[allow(non_snake_case, unused_mut, unused_variables)]
23        impl<Ctx, $($ty,)* $last> FromJobCallParts<Ctx> for ($($ty,)* $last,)
24        where
25            $( $ty: FromJobCallParts<Ctx> + Send, )*
26            $last: FromJobCallParts<Ctx> + Send,
27            Ctx: Send + Sync,
28        {
29            type Rejection = Option<JobResult>;
30
31            async fn from_job_call_parts(parts: &mut Parts, ctx: &Ctx) -> Result<Self, Self::Rejection> {
32                $(
33                    let $ty = $ty::from_job_call_parts(parts, ctx)
34                        .await
35                        .map_err(|err| err.into_job_result())?;
36                )*
37                let $last = $last::from_job_call_parts(parts, ctx)
38                    .await
39                    .map_err(|err| err.into_job_result())?;
40
41                Ok(($($ty,)* $last,))
42            }
43        }
44
45        // This impl must not be generic over M, otherwise it would conflict with the blanket
46        // implementation of `FromJobCall<S, Mut>` for `T: FromJobCallParts<S>`.
47        #[allow(non_snake_case, unused_mut, unused_variables)]
48        impl<Ctx, $($ty,)* $last> FromJobCall<Ctx> for ($($ty,)* $last,)
49        where
50            $( $ty: FromJobCallParts<Ctx> + Send, )*
51            $last: FromJobCall<Ctx> + Send,
52            Ctx: Send + Sync,
53        {
54            type Rejection = Option<JobResult>;
55
56            async fn from_job_call(call: JobCall, ctx: &Ctx) -> Result<Self, Self::Rejection> {
57                let (mut parts, body) = call.into_parts();
58
59                $(
60                    let $ty = $ty::from_job_call_parts(&mut parts, ctx).await.map_err(|err| err.into_job_result())?;
61                )*
62
63                let call = JobCall::from_parts(parts, body);
64
65                let $last = $last::from_job_call(call, ctx).await.map_err(|err| err.into_job_result())?;
66
67                Ok(($($ty,)* $last,))
68            }
69        }
70    };
71}
72
73all_the_tuples!(impl_from_job_call);
74
75#[cfg(test)]
76mod tests {
77    use bytes::Bytes;
78
79    use crate::extract::{FromJobCall, FromJobCallParts};
80    use crate::job::call::Parts as JobCallParts;
81
82    struct JobId;
83
84    impl<Ctx: Sync> FromJobCallParts<Ctx> for JobId {
85        type Rejection = ();
86        async fn from_job_call_parts(
87            _: &mut JobCallParts,
88            _: &Ctx,
89        ) -> Result<Self, Self::Rejection> {
90            Ok(Self)
91        }
92    }
93
94    fn assert_from_job_call<M, T>()
95    where
96        T: FromJobCall<(), M>,
97    {
98    }
99
100    fn assert_from_job_call_parts<T: FromJobCallParts<()>>() {}
101
102    #[test]
103    fn unit() {
104        assert_from_job_call_parts::<()>();
105        assert_from_job_call::<_, ()>();
106    }
107
108    #[test]
109    fn tuple_of_one() {
110        assert_from_job_call_parts::<(JobId,)>();
111        assert_from_job_call::<_, (JobId,)>();
112        assert_from_job_call::<_, (Bytes,)>();
113    }
114
115    #[test]
116    fn tuple_of_two() {
117        assert_from_job_call_parts::<((), ())>();
118        assert_from_job_call::<_, ((), ())>();
119        assert_from_job_call::<_, (JobId, Bytes)>();
120    }
121
122    #[test]
123    fn nested_tuple() {
124        assert_from_job_call_parts::<(((JobId,),),)>();
125        assert_from_job_call::<_, ((((Bytes,),),),)>();
126    }
127}