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}