apalis_core/
response.rs

1use std::{any::Any, fmt::Debug, sync::Arc};
2
3use crate::{
4    error::Error,
5    task::{attempt::Attempt, task_id::TaskId},
6};
7
8/// A generic `Response` struct that wraps the result of a task, containing the outcome (`Ok` or `Err`),
9/// task metadata such as `task_id`, `attempt`, and an internal marker field for future extensions.
10///
11/// # Type Parameters
12/// - `Res`: The successful result type of the response.
13///
14/// # Fields
15/// - `inner`: A `Result` that holds either the success value of type `Res` or an `Error` on failure.
16/// - `task_id`: A `TaskId` representing the unique identifier for the task.
17/// - `attempt`: An `Attempt` representing how many attempts were made to complete the task.
18/// - `_priv`: A private marker field to prevent external construction of the `Response`.
19#[derive(Debug, Clone)]
20pub struct Response<Res> {
21    /// The result from a task
22    pub inner: Result<Res, Error>,
23    /// The task id
24    pub task_id: TaskId,
25    /// The current attempt
26    pub attempt: Attempt,
27    pub(crate) _priv: (),
28}
29
30impl<Res> Response<Res> {
31    /// Creates a new `Response` instance.
32    ///
33    /// # Arguments
34    /// - `inner`: A `Result` holding either a successful response of type `Res` or an `Error`.
35    /// - `task_id`: A `TaskId` representing the unique identifier for the task.
36    /// - `attempt`: The attempt count when creating this response.
37    ///
38    /// # Returns
39    /// A new `Response` instance.
40    pub fn new(inner: Result<Res, Error>, task_id: TaskId, attempt: Attempt) -> Self {
41        Response {
42            inner,
43            task_id,
44            attempt,
45            _priv: (),
46        }
47    }
48
49    /// Constructs a successful `Response`.
50    ///
51    /// # Arguments
52    /// - `res`: The success value of type `Res`.
53    /// - `task_id`: A `TaskId` representing the unique identifier for the task.
54    /// - `attempt`: The attempt count when creating this response.
55    ///
56    /// # Returns
57    /// A `Response` instance containing the success value.
58    pub fn success(res: Res, task_id: TaskId, attempt: Attempt) -> Self {
59        Self::new(Ok(res), task_id, attempt)
60    }
61
62    /// Constructs a failed `Response`.
63    ///
64    /// # Arguments
65    /// - `error`: The `Error` that occurred.
66    /// - `task_id`: A `TaskId` representing the unique identifier for the task.
67    /// - `attempt`: The attempt count when creating this response.
68    ///
69    /// # Returns
70    /// A `Response` instance containing the error.
71    pub fn failure(error: Error, task_id: TaskId, attempt: Attempt) -> Self {
72        Self::new(Err(error), task_id, attempt)
73    }
74
75    /// Checks if the `Response` contains a success (`Ok`).
76    ///
77    /// # Returns
78    /// `true` if the `Response` is successful, `false` otherwise.
79    pub fn is_success(&self) -> bool {
80        self.inner.is_ok()
81    }
82
83    /// Checks if the `Response` contains a failure (`Err`).
84    ///
85    /// # Returns
86    /// `true` if the `Response` is a failure, `false` otherwise.
87    pub fn is_failure(&self) -> bool {
88        self.inner.is_err()
89    }
90
91    /// Maps the success value (`Res`) of the `Response` to another type using the provided function.
92    ///
93    /// # Arguments
94    /// - `f`: A function that takes a reference to the success value and returns a new value of type `T`.
95    ///
96    /// # Returns
97    /// A new `Response` with the transformed success value or the same error.
98    ///
99    /// # Type Parameters
100    /// - `F`: A function or closure that takes a reference to a value of type `Res` and returns a value of type `T`.
101    /// - `T`: The new type of the success value after mapping.
102    pub fn map<F, T>(&self, f: F) -> Response<T>
103    where
104        F: FnOnce(&Res) -> T,
105    {
106        Response {
107            inner: self.inner.as_ref().map(f).map_err(|e| e.clone()),
108            task_id: self.task_id.clone(),
109            attempt: self.attempt.clone(),
110            _priv: (),
111        }
112    }
113}
114
115/// Helper for Job Responses
116pub trait IntoResponse {
117    /// The final result of the job
118    type Result;
119    /// converts self into a Result
120    fn into_response(self) -> Self::Result;
121}
122
123impl IntoResponse for bool {
124    type Result = std::result::Result<Self, Error>;
125    fn into_response(self) -> std::result::Result<Self, Error> {
126        match self {
127            true => Ok(true),
128            false => Err(Error::Failed(Arc::new(Box::new(std::io::Error::new(
129                std::io::ErrorKind::Other,
130                "Job returned false",
131            ))))),
132        }
133    }
134}
135
136impl<T: Any, E: std::error::Error + Sync + Send + 'static + Any> IntoResponse
137    for std::result::Result<T, E>
138{
139    type Result = Result<T, Error>;
140    fn into_response(self) -> Result<T, Error> {
141        match self {
142            Ok(value) => Ok(value),
143            Err(e) => {
144                // Try to downcast the error to see if it is already of type `Error`
145                if let Some(custom_error) =
146                    (&e as &(dyn std::error::Error + 'static)).downcast_ref::<Error>()
147                {
148                    return Err(custom_error.clone());
149                }
150                Err(Error::Failed(Arc::new(Box::new(e))))
151            }
152        }
153    }
154}
155
156macro_rules! SIMPLE_JOB_RESULT {
157    ($type:ty) => {
158        impl IntoResponse for $type {
159            type Result = std::result::Result<$type, Error>;
160            fn into_response(self) -> std::result::Result<$type, Error> {
161                Ok(self)
162            }
163        }
164    };
165}
166
167SIMPLE_JOB_RESULT!(());
168SIMPLE_JOB_RESULT!(u8);
169SIMPLE_JOB_RESULT!(u16);
170SIMPLE_JOB_RESULT!(u32);
171SIMPLE_JOB_RESULT!(u64);
172SIMPLE_JOB_RESULT!(usize);
173SIMPLE_JOB_RESULT!(i8);
174SIMPLE_JOB_RESULT!(i16);
175SIMPLE_JOB_RESULT!(i32);
176SIMPLE_JOB_RESULT!(i64);
177SIMPLE_JOB_RESULT!(isize);
178SIMPLE_JOB_RESULT!(f32);
179SIMPLE_JOB_RESULT!(f64);
180SIMPLE_JOB_RESULT!(String);
181SIMPLE_JOB_RESULT!(&'static str);