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);