blueprint_core/ext_traits/
job_call.rs

1use crate::JobCall;
2use crate::extract::{FromJobCall, FromJobCallParts};
3use core::future::Future;
4
5mod sealed {
6    use crate::JobCall;
7
8    pub trait Sealed {}
9    impl Sealed for JobCall {}
10}
11
12/// Extension trait that adds additional methods to [`JobCall`].
13pub trait JobCallExt: sealed::Sealed + Sized {
14    /// Apply an extractor to this `JobCall`.
15    ///
16    /// This is just a convenience for `E::from_job_call(call, &())`.
17    ///
18    /// Note this consumes the job call. Use [`JobCallExt::extract_parts()`] if you're not extracting
19    /// the body and don't want to consume the job call.
20    fn extract<E, M>(self) -> impl Future<Output = Result<E, E::Rejection>> + Send
21    where
22        E: FromJobCall<(), M> + 'static,
23        M: 'static;
24
25    /// Apply an extractor that requires some context to this `JobCall`.
26    ///
27    /// This is just a convenience for `E::from_job_call(call, ctx)`.
28    ///
29    /// Note this consumes the job call. Use [`JobCallExt::extract_parts_with_context()`] if you're not
30    /// extracting the body and don't want to consume the job call.
31    ///
32    /// # Example
33    ///
34    /// ```
35    /// use blueprint_sdk::extract::{FromJobCall, FromRef};
36    /// use blueprint_sdk::{JobCall, JobCallExt};
37    ///
38    /// struct MyExtractor {
39    ///     requires_context: RequiresContext,
40    /// }
41    ///
42    /// impl<Ctx> FromJobCall<Ctx> for MyExtractor
43    /// where
44    ///     String: FromRef<Ctx>,
45    ///     Ctx: Send + Sync,
46    /// {
47    ///     type Rejection = std::convert::Infallible;
48    ///
49    ///     async fn from_job_call(call: JobCall, context: &Ctx) -> Result<Self, Self::Rejection> {
50    ///         let requires_context = call.extract_with_context::<RequiresContext, _, _>(context).await?;
51    ///
52    ///         Ok(Self { requires_context })
53    ///     }
54    /// }
55    ///
56    /// // some extractor that consumes the call body and requires a context
57    /// struct RequiresContext { /* ... */ }
58    ///
59    /// impl<Ctx> FromJobCall<Ctx> for RequiresContext
60    /// where
61    ///     String: FromRef<Ctx>,
62    ///     Ctx: Send + Sync,
63    /// {
64    ///     // ...
65    ///     # type Rejection = std::convert::Infallible;
66    ///     # async fn from_job_call(call: JobCall, _context: &Ctx) -> Result<Self, Self::Rejection> {
67    ///     #     unimplemented!()
68    ///     # }
69    /// }
70    /// ```
71    fn extract_with_context<E, Ctx, M>(
72        self,
73        ctx: &Ctx,
74    ) -> impl Future<Output = Result<E, E::Rejection>> + Send
75    where
76        E: FromJobCall<Ctx, M> + 'static,
77        Ctx: Send + Sync;
78
79    /// Apply a parts extractor to this `JobCall`.
80    ///
81    /// This is just a convenience for `E::from_job_call_parts(parts, ctx)`.
82    fn extract_parts<E>(&mut self) -> impl Future<Output = Result<E, E::Rejection>> + Send
83    where
84        E: FromJobCallParts<()> + 'static;
85
86    /// Apply a parts extractor that requires some state to this `Request`.
87    ///
88    /// This is just a convenience for `E::from_job_call_parts(parts, ctx)`.
89    fn extract_parts_with_context<'a, E, Ctx>(
90        &'a mut self,
91        ctx: &'a Ctx,
92    ) -> impl Future<Output = Result<E, E::Rejection>> + Send + 'a
93    where
94        E: FromJobCallParts<Ctx> + 'static,
95        Ctx: Send + Sync;
96}
97
98impl JobCallExt for JobCall {
99    fn extract<E, M>(self) -> impl Future<Output = Result<E, E::Rejection>> + Send
100    where
101        E: FromJobCall<(), M> + 'static,
102        M: 'static,
103    {
104        self.extract_with_context(&())
105    }
106
107    fn extract_with_context<E, Ctx, M>(
108        self,
109        ctx: &Ctx,
110    ) -> impl Future<Output = Result<E, E::Rejection>> + Send
111    where
112        E: FromJobCall<Ctx, M> + 'static,
113        Ctx: Send + Sync,
114    {
115        E::from_job_call(self, ctx)
116    }
117
118    fn extract_parts<E>(&mut self) -> impl Future<Output = Result<E, E::Rejection>> + Send
119    where
120        E: FromJobCallParts<()> + 'static,
121    {
122        self.extract_parts_with_context(&())
123    }
124
125    async fn extract_parts_with_context<'a, E, Ctx>(
126        &'a mut self,
127        ctx: &'a Ctx,
128    ) -> Result<E, E::Rejection>
129    where
130        E: FromJobCallParts<Ctx> + 'static,
131        Ctx: Send + Sync,
132    {
133        let mut call = crate::job_call::JobCall::default();
134        *call.job_id_mut() = self.job_id();
135        *call.metadata_mut() = core::mem::take(self.metadata_mut());
136        let (mut parts, ()) = call.into_parts();
137
138        let result = E::from_job_call_parts(&mut parts, ctx).await;
139
140        *self.job_id_mut() = parts.job_id;
141        *self.metadata_mut() = core::mem::take(&mut parts.metadata);
142
143        result
144    }
145}