Skip to main content

scheduler/
error.rs

1use std::error::Error;
2use std::fmt::{self, Display, Formatter};
3use tokio::task::JoinError;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum InvalidJobKind {
7    ZeroInterval,
8    IntervalOutOfRange,
9    CronExpression,
10    Other,
11}
12
13#[derive(Debug)]
14pub struct InvalidJobError {
15    kind: InvalidJobKind,
16    message: String,
17}
18
19impl InvalidJobError {
20    pub fn kind(&self) -> InvalidJobKind {
21        self.kind
22    }
23
24    pub fn message(&self) -> &str {
25        &self.message
26    }
27}
28
29impl Display for InvalidJobError {
30    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
31        f.write_str(&self.message)
32    }
33}
34
35impl Error for InvalidJobError {}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum StoreErrorKind {
39    Connection,
40    Data,
41    Unknown,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum ExecutionGuardErrorKind {
46    Connection,
47    Data,
48    Unknown,
49}
50
51#[derive(Debug)]
52pub struct StoreError {
53    kind: StoreErrorKind,
54    source: Box<dyn Error + Send + Sync>,
55}
56
57impl StoreError {
58    pub fn new<E>(source: E, kind: StoreErrorKind) -> Self
59    where
60        E: Error + Send + Sync + 'static,
61    {
62        Self {
63            kind,
64            source: Box::new(source),
65        }
66    }
67
68    pub fn kind(&self) -> StoreErrorKind {
69        self.kind
70    }
71}
72
73impl Display for StoreError {
74    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
75        write!(f, "{}", self.source)
76    }
77}
78
79impl Error for StoreError {
80    fn source(&self) -> Option<&(dyn Error + 'static)> {
81        Some(self.source.as_ref())
82    }
83}
84
85#[derive(Debug)]
86pub struct ExecutionGuardError {
87    kind: ExecutionGuardErrorKind,
88    source: Box<dyn Error + Send + Sync>,
89}
90
91impl ExecutionGuardError {
92    pub fn new<E>(source: E, kind: ExecutionGuardErrorKind) -> Self
93    where
94        E: Error + Send + Sync + 'static,
95    {
96        Self {
97            kind,
98            source: Box::new(source),
99        }
100    }
101
102    pub fn kind(&self) -> ExecutionGuardErrorKind {
103        self.kind
104    }
105}
106
107impl Display for ExecutionGuardError {
108    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
109        write!(f, "{}", self.source)
110    }
111}
112
113impl Error for ExecutionGuardError {
114    fn source(&self) -> Option<&(dyn Error + 'static)> {
115        Some(self.source.as_ref())
116    }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum TaskJoinErrorKind {
121    Cancelled,
122    Panic,
123    Unknown,
124}
125
126#[derive(Debug)]
127pub struct TaskJoinError {
128    kind: TaskJoinErrorKind,
129    message: String,
130}
131
132impl TaskJoinError {
133    pub fn kind(&self) -> TaskJoinErrorKind {
134        self.kind
135    }
136
137    pub fn message(&self) -> &str {
138        &self.message
139    }
140}
141
142impl Display for TaskJoinError {
143    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
144        f.write_str(&self.message)
145    }
146}
147
148impl Error for TaskJoinError {}
149
150#[derive(Debug)]
151pub enum SchedulerError {
152    InvalidJob(InvalidJobError),
153    Store(StoreError),
154    ExecutionGuard(ExecutionGuardError),
155    TaskJoin(TaskJoinError),
156}
157
158impl Display for SchedulerError {
159    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
160        match self {
161            SchedulerError::InvalidJob(message) => write!(f, "invalid job: {message}"),
162            SchedulerError::Store(error) => write!(f, "state store error: {error}"),
163            SchedulerError::ExecutionGuard(error) => {
164                write!(f, "execution guard error: {error}")
165            }
166            SchedulerError::TaskJoin(message) => write!(f, "task join error: {message}"),
167        }
168    }
169}
170
171impl Error for SchedulerError {}
172
173impl SchedulerError {
174    pub(crate) fn invalid_zero_interval() -> Self {
175        Self::invalid_job_with_kind(
176            InvalidJobKind::ZeroInterval,
177            "interval schedule must be greater than zero",
178        )
179    }
180
181    pub(crate) fn invalid_interval_out_of_range() -> Self {
182        Self::invalid_job_with_kind(
183            InvalidJobKind::IntervalOutOfRange,
184            "interval schedule is too large to represent",
185        )
186    }
187
188    pub(crate) fn invalid_cron(message: impl Into<String>) -> Self {
189        Self::invalid_job_with_kind(InvalidJobKind::CronExpression, message)
190    }
191
192    pub(crate) fn invalid_job_with_kind(kind: InvalidJobKind, message: impl Into<String>) -> Self {
193        Self::InvalidJob(InvalidJobError {
194            kind,
195            message: message.into(),
196        })
197    }
198
199    pub(crate) fn store<E>(error: E, kind: StoreErrorKind) -> Self
200    where
201        E: Error + Send + Sync + 'static,
202    {
203        Self::Store(StoreError::new(error, kind))
204    }
205
206    pub(crate) fn task_join(error: JoinError) -> Self {
207        let kind = if error.is_cancelled() {
208            TaskJoinErrorKind::Cancelled
209        } else if error.is_panic() {
210            TaskJoinErrorKind::Panic
211        } else {
212            TaskJoinErrorKind::Unknown
213        };
214
215        Self::TaskJoin(TaskJoinError {
216            kind,
217            message: error.to_string(),
218        })
219    }
220
221    pub(crate) fn execution_guard<E>(error: E, kind: ExecutionGuardErrorKind) -> Self
222    where
223        E: Error + Send + Sync + 'static,
224    {
225        Self::ExecutionGuard(ExecutionGuardError::new(error, kind))
226    }
227}