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