blueprint_core/extract/
mod.rs

1//! Types and traits for extracting data from requests.
2//!
3//! See [`blueprint_sdk::extract`] for more details.
4//!
5//! [`blueprint_sdk::extract`]: https://docs.rs/blueprint_sdk/latest/blueprint_sdk/extract/index.html
6
7use crate::JobCall;
8use crate::job::call::Parts;
9use crate::job::result::IntoJobResult;
10use core::convert::Infallible;
11
12pub mod rejection;
13
14mod context;
15mod extension;
16mod from_ref;
17mod job_call_parts;
18mod job_id;
19mod metadata;
20mod option;
21mod tuple;
22
23pub use self::{
24    context::Context,
25    extension::{Extension, ExtensionNotFound},
26    from_ref::FromRef,
27    job_id::JobId,
28    metadata::Metadata,
29    option::{OptionalFromJobCall, OptionalFromJobCallParts},
30    rejection::InvalidUtf8,
31};
32
33mod private {
34    #[derive(Debug, Clone, Copy)]
35    pub enum ViaParts {}
36
37    #[derive(Debug, Clone, Copy)]
38    pub enum ViaJobCall {}
39}
40
41/// Types that can be created from job calls.
42///
43/// Extractors that implement `FromJobCall` can consume the job call body and can thus only be run
44/// once for jobs.
45///
46/// If your extractor doesn't need to consume the job call body then you should implement
47/// [`FromJobCallParts`] and not [`FromJobCall`].
48///
49/// See [`blueprint_sdk::extract`] for more general docs about extractors.
50///
51/// [`blueprint_sdk::extract`]: https://docs.rs/blueprint_sdk/latest/blueprint_sdk/extract/index.html
52#[diagnostic::on_unimplemented(
53    note = "Function argument is not a valid extractor. \nSee `https://docs.rs/blueprint_sdk/latest/blueprint_sdk/extract/index.html` for details"
54)]
55pub trait FromJobCallParts<Ctx>: Sized {
56    /// If the extractor fails it'll use this "rejection" type. A rejection is
57    /// a kind of error that can be converted into a job result.
58    type Rejection: IntoJobResult;
59
60    /// Perform the extraction.
61    fn from_job_call_parts(
62        parts: &mut Parts,
63        ctx: &Ctx,
64    ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send;
65}
66
67/// Types that can be created from job calls.
68///
69/// Extractors that implement `FromJobCall` can consume the job call body and can thus only be run
70/// once for jobs.
71///
72/// If your extractor doesn't need to consume the job call body then you should implement
73/// [`FromJobCallParts`] and not [`FromJobCall`].
74///
75/// See [`blueprint_sdk::extract`] for more general docs about extractors.
76///
77/// [`blueprint_sdk::extract`]: https://docs.rs/blueprint_sdk/latest/blueprint_sdk/extract/index.html
78#[diagnostic::on_unimplemented(
79    note = "Function argument is not a valid extractor. \nSee `https://docs.rs/blueprint_sdk/latest/blueprint_sdk/extract/index.html` for details"
80)]
81pub trait FromJobCall<Ctx, M = private::ViaJobCall>: Sized {
82    /// If the extractor fails it'll use this "rejection" type. A rejection is
83    /// a kind of error that can be converted into a job result.
84    type Rejection: IntoJobResult;
85
86    /// Perform the extraction.
87    fn from_job_call(
88        call: JobCall,
89        ctx: &Ctx,
90    ) -> impl Future<Output = Result<Self, Self::Rejection>> + Send;
91}
92
93impl<Ctx, T> FromJobCall<Ctx, private::ViaParts> for T
94where
95    Ctx: Send + Sync,
96    T: FromJobCallParts<Ctx>,
97{
98    type Rejection = <Self as FromJobCallParts<Ctx>>::Rejection;
99
100    async fn from_job_call(call: JobCall, ctx: &Ctx) -> Result<Self, Self::Rejection> {
101        let (mut parts, _) = call.into_parts();
102        Self::from_job_call_parts(&mut parts, ctx).await
103    }
104}
105
106impl<Ctx, T> FromJobCallParts<Ctx> for Result<T, T::Rejection>
107where
108    T: FromJobCallParts<Ctx>,
109    Ctx: Send + Sync,
110{
111    type Rejection = Infallible;
112
113    async fn from_job_call_parts(parts: &mut Parts, ctx: &Ctx) -> Result<Self, Self::Rejection> {
114        Ok(T::from_job_call_parts(parts, ctx).await)
115    }
116}
117
118impl<Ctx, T> FromJobCall<Ctx> for Result<T, T::Rejection>
119where
120    T: FromJobCall<Ctx>,
121    Ctx: Send + Sync,
122{
123    type Rejection = Infallible;
124
125    async fn from_job_call(call: JobCall, ctx: &Ctx) -> Result<Self, Self::Rejection> {
126        Ok(T::from_job_call(call, ctx).await)
127    }
128}