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}