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