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)]
45pub struct StoreError {
46    kind: StoreErrorKind,
47    source: Box<dyn Error + Send + Sync>,
48}
49
50impl StoreError {
51    pub fn new<E>(source: E, kind: StoreErrorKind) -> Self
52    where
53        E: Error + Send + Sync + 'static,
54    {
55        Self {
56            kind,
57            source: Box::new(source),
58        }
59    }
60
61    pub fn kind(&self) -> StoreErrorKind {
62        self.kind
63    }
64}
65
66impl Display for StoreError {
67    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
68        write!(f, "{}", self.source)
69    }
70}
71
72impl Error for StoreError {
73    fn source(&self) -> Option<&(dyn Error + 'static)> {
74        Some(self.source.as_ref())
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum TaskJoinErrorKind {
80    Cancelled,
81    Panic,
82    Unknown,
83}
84
85#[derive(Debug)]
86pub struct TaskJoinError {
87    kind: TaskJoinErrorKind,
88    message: String,
89}
90
91impl TaskJoinError {
92    pub fn kind(&self) -> TaskJoinErrorKind {
93        self.kind
94    }
95
96    pub fn message(&self) -> &str {
97        &self.message
98    }
99}
100
101impl Display for TaskJoinError {
102    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
103        f.write_str(&self.message)
104    }
105}
106
107impl Error for TaskJoinError {}
108
109#[derive(Debug)]
110pub enum SchedulerError {
111    InvalidJob(InvalidJobError),
112    Store(StoreError),
113    TaskJoin(TaskJoinError),
114}
115
116impl Display for SchedulerError {
117    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
118        match self {
119            SchedulerError::InvalidJob(message) => write!(f, "invalid job: {message}"),
120            SchedulerError::Store(error) => write!(f, "state store error: {error}"),
121            SchedulerError::TaskJoin(message) => write!(f, "task join error: {message}"),
122        }
123    }
124}
125
126impl Error for SchedulerError {}
127
128impl SchedulerError {
129    pub(crate) fn invalid_zero_interval() -> Self {
130        Self::invalid_job_with_kind(
131            InvalidJobKind::ZeroInterval,
132            "interval schedule must be greater than zero",
133        )
134    }
135
136    pub(crate) fn invalid_interval_out_of_range() -> Self {
137        Self::invalid_job_with_kind(
138            InvalidJobKind::IntervalOutOfRange,
139            "interval schedule is too large to represent",
140        )
141    }
142
143    pub(crate) fn invalid_cron(message: impl Into<String>) -> Self {
144        Self::invalid_job_with_kind(InvalidJobKind::CronExpression, message)
145    }
146
147    pub(crate) fn invalid_job_with_kind(kind: InvalidJobKind, message: impl Into<String>) -> Self {
148        Self::InvalidJob(InvalidJobError {
149            kind,
150            message: message.into(),
151        })
152    }
153
154    pub(crate) fn store<E>(error: E, kind: StoreErrorKind) -> Self
155    where
156        E: Error + Send + Sync + 'static,
157    {
158        Self::Store(StoreError::new(error, kind))
159    }
160
161    pub(crate) fn task_join(error: JoinError) -> Self {
162        let kind = if error.is_cancelled() {
163            TaskJoinErrorKind::Cancelled
164        } else if error.is_panic() {
165            TaskJoinErrorKind::Panic
166        } else {
167            TaskJoinErrorKind::Unknown
168        };
169
170        Self::TaskJoin(TaskJoinError {
171            kind,
172            message: error.to_string(),
173        })
174    }
175}