apalis_core/
response.rs

1use std::{fmt::Debug, sync::Arc};
2
3use crate::{
4    error::{BoxDynError, 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, E: Into<BoxDynError>> IntoResponse for std::result::Result<T, E> {
137    type Result = Result<T, Error>;
138    fn into_response(self) -> Result<T, Error> {
139        match self {
140            Ok(value) => Ok(value),
141            Err(e) => {
142                let e = e.into();
143                if let Some(custom_error) = e.downcast_ref::<Error>() {
144                    return Err(custom_error.clone());
145                }
146                Err(Error::Failed(Arc::new(e)))
147            }
148        }
149    }
150}
151
152macro_rules! SIMPLE_JOB_RESULT {
153    ($type:ty) => {
154        impl IntoResponse for $type {
155            type Result = std::result::Result<$type, Error>;
156            fn into_response(self) -> std::result::Result<$type, Error> {
157                Ok(self)
158            }
159        }
160    };
161}
162
163SIMPLE_JOB_RESULT!(());
164SIMPLE_JOB_RESULT!(u8);
165SIMPLE_JOB_RESULT!(u16);
166SIMPLE_JOB_RESULT!(u32);
167SIMPLE_JOB_RESULT!(u64);
168SIMPLE_JOB_RESULT!(usize);
169SIMPLE_JOB_RESULT!(i8);
170SIMPLE_JOB_RESULT!(i16);
171SIMPLE_JOB_RESULT!(i32);
172SIMPLE_JOB_RESULT!(i64);
173SIMPLE_JOB_RESULT!(isize);
174SIMPLE_JOB_RESULT!(f32);
175SIMPLE_JOB_RESULT!(f64);
176SIMPLE_JOB_RESULT!(String);
177SIMPLE_JOB_RESULT!(&'static str);