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}