foxtive_supervisor/
error.rs1use thiserror::Error;
4
5pub type SupervisorResult<T> = Result<T, SupervisorError>;
7
8#[derive(Error, Debug)]
10pub enum SupervisorError {
11 #[error("Configuration error for task '{task_id}': {reason}")]
12 ConfigurationError { task_id: String, reason: String },
13
14 #[error("Dependency validation failed for task '{task_id}': '{dependency_id}' - {error:?}")]
15 DependencyValidation {
16 task_id: String,
17 dependency_id: String,
18 error: ValidationError,
19 },
20
21 #[error("Circular dependency detected between '{task_a}' and '{task_b}'")]
22 CircularDependency { task_a: String, task_b: String },
23
24 #[error("Prerequisite '{name}' failed")]
25 PrerequisiteFailed {
26 name: String,
27 #[source]
28 source: Box<dyn std::error::Error + Send + Sync>,
29 },
30
31 #[error("Task '{0}' not found")]
32 UnknownTask(String),
33
34 #[error("Persistence error: {message}")]
35 PersistenceError {
36 message: String,
37 #[source]
38 source: std::io::Error,
39 },
40
41 #[error("Serialization error: {0}")]
42 SerializationError(#[from] serde_json::Error),
43
44 #[error("Task panicked: {task_id}")]
45 TaskPanicked {
46 task_id: String,
47 #[source]
48 source: tokio::task::JoinError,
49 },
50
51 #[error("Channel error: {0}")]
52 ChannelError(String),
53
54 #[error("Timeout: {0}")]
55 Timeout(String),
56
57 #[error("Coordination error: {0}")]
58 CoordinationError(String),
59
60 #[error("{0}")]
61 Internal(String),
62
63 #[error("{message}")]
66 Infrastructure {
67 message: String,
68 #[source]
69 source: Option<Box<dyn std::error::Error + Send + Sync>>,
70 },
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum ValidationError {
75 UnknownTaskId,
76 CircularDependency,
77}
78
79impl SupervisorError {
80 pub fn config(task_id: impl Into<String>, reason: impl Into<String>) -> Self {
81 Self::ConfigurationError {
82 task_id: task_id.into(),
83 reason: reason.into(),
84 }
85 }
86
87 pub fn dependency_validation(
88 task_id: &str,
89 dependency_id: &str,
90 error: ValidationError,
91 ) -> Self {
92 Self::DependencyValidation {
93 task_id: task_id.to_string(),
94 dependency_id: dependency_id.to_string(),
95 error,
96 }
97 }
98
99 pub fn circular_dependency(task_a: &str, task_b: &str) -> Self {
100 Self::CircularDependency {
101 task_a: task_a.to_string(),
102 task_b: task_b.to_string(),
103 }
104 }
105
106 pub fn prerequisite_failed(
107 name: &str,
108 source: Box<dyn std::error::Error + Send + Sync>,
109 ) -> Self {
110 Self::PrerequisiteFailed {
111 name: name.to_string(),
112 source,
113 }
114 }
115
116 pub fn unknown_task(id: &str) -> Self {
117 Self::UnknownTask(id.to_string())
118 }
119
120 pub fn persistence(message: impl Into<String>, source: std::io::Error) -> Self {
121 Self::PersistenceError {
122 message: message.into(),
123 source,
124 }
125 }
126
127 pub fn task_panicked(task_id: impl Into<String>, source: tokio::task::JoinError) -> Self {
128 Self::TaskPanicked {
129 task_id: task_id.into(),
130 source,
131 }
132 }
133
134 pub fn channel(message: impl Into<String>) -> Self {
135 Self::ChannelError(message.into())
136 }
137
138 pub fn timeout(message: impl Into<String>) -> Self {
139 Self::Timeout(message.into())
140 }
141
142 pub fn coordination(message: impl Into<String>) -> Self {
143 Self::CoordinationError(message.into())
144 }
145
146 pub fn internal(message: impl Into<String>) -> Self {
147 Self::Internal(message.into())
148 }
149
150 pub fn wrap(e: impl std::error::Error + Send + Sync + 'static) -> Self {
164 Self::Infrastructure {
165 message: format!("{e}"),
166 source: Some(Box::new(e)),
167 }
168 }
169}
170
171impl From<String> for SupervisorError {
172 fn from(msg: String) -> Self {
173 SupervisorError::Internal(msg)
174 }
175}
176
177impl From<&str> for SupervisorError {
178 fn from(msg: &str) -> Self {
179 SupervisorError::Internal(msg.to_string())
180 }
181}
182
183impl From<std::io::Error> for SupervisorError {
184 fn from(err: std::io::Error) -> Self {
185 SupervisorError::PersistenceError {
186 message: err.to_string(),
187 source: err,
188 }
189 }
190}
191
192impl From<tokio::task::JoinError> for SupervisorError {
193 fn from(err: tokio::task::JoinError) -> Self {
194 if err.is_panic() {
195 SupervisorError::TaskPanicked {
196 task_id: "unknown".to_string(),
197 source: err,
198 }
199 } else {
200 SupervisorError::ChannelError(format!("Task cancelled: {err}"))
201 }
202 }
203}
204
205#[cfg(feature = "distributed")]
206impl From<redis::RedisError> for SupervisorError {
207 fn from(err: redis::RedisError) -> Self {
208 SupervisorError::CoordinationError(err.to_string())
209 }
210}