Skip to main content

oxigdal_workflow/
error.rs

1//! Error types for the workflow engine.
2
3/// Result type for workflow operations.
4pub type Result<T> = std::result::Result<T, WorkflowError>;
5
6/// Comprehensive error types for the workflow engine.
7#[derive(Debug, thiserror::Error)]
8pub enum WorkflowError {
9    /// Workflow execution error.
10    #[error("Workflow execution error: {0}")]
11    Execution(String),
12
13    /// Workflow validation error.
14    #[error("Workflow validation error: {0}")]
15    Validation(String),
16
17    /// Workflow scheduling error.
18    #[error("Workflow scheduling error: {0}")]
19    Scheduling(String),
20
21    /// DAG construction error.
22    #[error("DAG error: {0}")]
23    Dag(#[from] DagError),
24
25    /// Task execution error.
26    #[error("Task execution error in '{task_id}': {message}")]
27    TaskExecution {
28        /// Task identifier.
29        task_id: String,
30        /// Error message.
31        message: String,
32    },
33
34    /// Task timeout error.
35    #[error("Task '{task_id}' timed out after {timeout_secs}s")]
36    TaskTimeout {
37        /// Task identifier.
38        task_id: String,
39        /// Timeout duration in seconds.
40        timeout_secs: u64,
41    },
42
43    /// Workflow state error.
44    #[error("Workflow state error: {0}")]
45    State(String),
46
47    /// Workflow not found error.
48    #[error("Workflow not found: {0}")]
49    NotFound(String),
50
51    /// Workflow already exists error.
52    #[error("Workflow already exists: {0}")]
53    AlreadyExists(String),
54
55    /// Conditional expression error.
56    #[error("Conditional expression error: {0}")]
57    ConditionalExpression(String),
58
59    /// Template error.
60    #[error("Template error: {0}")]
61    Template(String),
62
63    /// Versioning error.
64    #[error("Versioning error: {0}")]
65    Versioning(String),
66
67    /// Integration error.
68    #[error("Integration error with {service}: {message}")]
69    Integration {
70        /// Service name.
71        service: String,
72        /// Error message.
73        message: String,
74    },
75
76    /// Serialization error.
77    #[error("Serialization error: {0}")]
78    Serialization(#[from] serde_json::Error),
79
80    /// I/O error.
81    #[error("I/O error: {0}")]
82    Io(#[from] std::io::Error),
83
84    /// Cron expression error.
85    #[error("Invalid cron expression: {0}")]
86    CronExpression(String),
87
88    /// Persistence error.
89    #[error("Persistence error: {0}")]
90    Persistence(String),
91
92    /// Monitoring error.
93    #[error("Monitoring error: {0}")]
94    Monitoring(String),
95
96    /// Resource exhaustion error.
97    #[error("Resource exhausted: {0}")]
98    ResourceExhausted(String),
99
100    /// Deadlock detected error.
101    #[error("Deadlock detected in workflow '{workflow_id}'")]
102    Deadlock {
103        /// Workflow identifier.
104        workflow_id: String,
105    },
106
107    /// Invalid parameter error.
108    #[error("Invalid parameter '{param}': {message}")]
109    InvalidParameter {
110        /// Parameter name.
111        param: String,
112        /// Error message.
113        message: String,
114    },
115
116    /// Internal error.
117    #[error("Internal error: {0}")]
118    Internal(String),
119}
120
121/// DAG-specific errors.
122#[derive(Debug, thiserror::Error)]
123pub enum DagError {
124    /// Cycle detected in DAG.
125    #[error("Cycle detected in DAG: {0}")]
126    CycleDetected(String),
127
128    /// Invalid node ID.
129    #[error("Invalid node ID: {0}")]
130    InvalidNode(String),
131
132    /// Invalid edge.
133    #[error("Invalid edge from '{from}' to '{to}': {message}")]
134    InvalidEdge {
135        /// Source node ID.
136        from: String,
137        /// Target node ID.
138        to: String,
139        /// Error message.
140        message: String,
141    },
142
143    /// Empty DAG error.
144    #[error("DAG is empty")]
145    EmptyDag,
146
147    /// Unreachable node error.
148    #[error("Unreachable node: {0}")]
149    UnreachableNode(String),
150
151    /// Missing dependency error.
152    #[error("Missing dependency: {0}")]
153    MissingDependency(String),
154}
155
156impl WorkflowError {
157    /// Create an execution error.
158    pub fn execution<S: Into<String>>(msg: S) -> Self {
159        Self::Execution(msg.into())
160    }
161
162    /// Create a validation error.
163    pub fn validation<S: Into<String>>(msg: S) -> Self {
164        Self::Validation(msg.into())
165    }
166
167    /// Create a scheduling error.
168    pub fn scheduling<S: Into<String>>(msg: S) -> Self {
169        Self::Scheduling(msg.into())
170    }
171
172    /// Create a state error.
173    pub fn state<S: Into<String>>(msg: S) -> Self {
174        Self::State(msg.into())
175    }
176
177    /// Create a not found error.
178    pub fn not_found<S: Into<String>>(id: S) -> Self {
179        Self::NotFound(id.into())
180    }
181
182    /// Create an already exists error.
183    pub fn already_exists<S: Into<String>>(id: S) -> Self {
184        Self::AlreadyExists(id.into())
185    }
186
187    /// Create a task execution error.
188    pub fn task_execution<S1: Into<String>, S2: Into<String>>(task_id: S1, message: S2) -> Self {
189        Self::TaskExecution {
190            task_id: task_id.into(),
191            message: message.into(),
192        }
193    }
194
195    /// Create a task timeout error.
196    pub fn task_timeout<S: Into<String>>(task_id: S, timeout_secs: u64) -> Self {
197        Self::TaskTimeout {
198            task_id: task_id.into(),
199            timeout_secs,
200        }
201    }
202
203    /// Create a conditional expression error.
204    pub fn conditional<S: Into<String>>(msg: S) -> Self {
205        Self::ConditionalExpression(msg.into())
206    }
207
208    /// Create a template error.
209    pub fn template<S: Into<String>>(msg: S) -> Self {
210        Self::Template(msg.into())
211    }
212
213    /// Create a versioning error.
214    pub fn versioning<S: Into<String>>(msg: S) -> Self {
215        Self::Versioning(msg.into())
216    }
217
218    /// Create an integration error.
219    pub fn integration<S1: Into<String>, S2: Into<String>>(service: S1, message: S2) -> Self {
220        Self::Integration {
221            service: service.into(),
222            message: message.into(),
223        }
224    }
225
226    /// Create a cron expression error.
227    pub fn cron_expression<S: Into<String>>(msg: S) -> Self {
228        Self::CronExpression(msg.into())
229    }
230
231    /// Create a persistence error.
232    pub fn persistence<S: Into<String>>(msg: S) -> Self {
233        Self::Persistence(msg.into())
234    }
235
236    /// Create a monitoring error.
237    pub fn monitoring<S: Into<String>>(msg: S) -> Self {
238        Self::Monitoring(msg.into())
239    }
240
241    /// Create a resource exhausted error.
242    pub fn resource_exhausted<S: Into<String>>(msg: S) -> Self {
243        Self::ResourceExhausted(msg.into())
244    }
245
246    /// Create a deadlock error.
247    pub fn deadlock<S: Into<String>>(workflow_id: S) -> Self {
248        Self::Deadlock {
249            workflow_id: workflow_id.into(),
250        }
251    }
252
253    /// Create an invalid parameter error.
254    pub fn invalid_parameter<S1: Into<String>, S2: Into<String>>(param: S1, message: S2) -> Self {
255        Self::InvalidParameter {
256            param: param.into(),
257            message: message.into(),
258        }
259    }
260
261    /// Create an internal error.
262    pub fn internal<S: Into<String>>(msg: S) -> Self {
263        Self::Internal(msg.into())
264    }
265}
266
267impl DagError {
268    /// Create a cycle detected error.
269    pub fn cycle<S: Into<String>>(path: S) -> Self {
270        Self::CycleDetected(path.into())
271    }
272
273    /// Create an invalid node error.
274    pub fn invalid_node<S: Into<String>>(node_id: S) -> Self {
275        Self::InvalidNode(node_id.into())
276    }
277
278    /// Create an invalid edge error.
279    pub fn invalid_edge<S1: Into<String>, S2: Into<String>, S3: Into<String>>(
280        from: S1,
281        to: S2,
282        message: S3,
283    ) -> Self {
284        Self::InvalidEdge {
285            from: from.into(),
286            to: to.into(),
287            message: message.into(),
288        }
289    }
290
291    /// Create a missing dependency error.
292    pub fn missing_dependency<S: Into<String>>(dep: S) -> Self {
293        Self::MissingDependency(dep.into())
294    }
295}