Skip to main content

erio_workflow/
error.rs

1//! Error types for the workflow engine.
2
3use thiserror::Error;
4
5fn cycle_display(steps: &[String]) -> String {
6    steps.join(" -> ")
7}
8
9/// Errors from the workflow engine.
10#[derive(Debug, Error)]
11pub enum WorkflowError {
12    #[error("Cycle detected: {}", cycle_display(.steps))]
13    CycleDetected { steps: Vec<String> },
14
15    #[error("Step not found: {step_id}")]
16    StepNotFound { step_id: String },
17
18    #[error("Duplicate step: {step_id}")]
19    DuplicateStep { step_id: String },
20
21    #[error("Step '{step_id}' failed: {message}")]
22    StepFailed { step_id: String, message: String },
23
24    #[error("Step '{step_id}' skipped: dependency '{dependency_id}' failed")]
25    DependencyFailed {
26        step_id: String,
27        dependency_id: String,
28    },
29
30    #[error("Step '{step_id}' depends on unknown step '{dependency_id}'")]
31    MissingDependency {
32        step_id: String,
33        dependency_id: String,
34    },
35
36    #[error("Workflow has no steps")]
37    EmptyWorkflow,
38
39    #[error("Checkpoint error: {message}")]
40    Checkpoint { message: String },
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    // === Display Tests ===
48
49    #[test]
50    fn cycle_detected_displays_steps() {
51        let err = WorkflowError::CycleDetected {
52            steps: vec!["a".into(), "b".into(), "a".into()],
53        };
54        assert_eq!(err.to_string(), "Cycle detected: a -> b -> a");
55    }
56
57    #[test]
58    fn step_not_found_displays_id() {
59        let err = WorkflowError::StepNotFound {
60            step_id: "missing".into(),
61        };
62        assert_eq!(err.to_string(), "Step not found: missing");
63    }
64
65    #[test]
66    fn duplicate_step_displays_id() {
67        let err = WorkflowError::DuplicateStep {
68            step_id: "dup".into(),
69        };
70        assert_eq!(err.to_string(), "Duplicate step: dup");
71    }
72
73    #[test]
74    fn step_failed_displays_id_and_message() {
75        let err = WorkflowError::StepFailed {
76            step_id: "build".into(),
77            message: "compile error".into(),
78        };
79        assert_eq!(err.to_string(), "Step 'build' failed: compile error");
80    }
81
82    #[test]
83    fn dependency_failed_displays_ids() {
84        let err = WorkflowError::DependencyFailed {
85            step_id: "deploy".into(),
86            dependency_id: "build".into(),
87        };
88        assert_eq!(
89            err.to_string(),
90            "Step 'deploy' skipped: dependency 'build' failed"
91        );
92    }
93
94    #[test]
95    fn missing_dependency_displays_ids() {
96        let err = WorkflowError::MissingDependency {
97            step_id: "b".into(),
98            dependency_id: "unknown".into(),
99        };
100        assert_eq!(
101            err.to_string(),
102            "Step 'b' depends on unknown step 'unknown'"
103        );
104    }
105
106    #[test]
107    fn empty_workflow_displays_message() {
108        let err = WorkflowError::EmptyWorkflow;
109        assert_eq!(err.to_string(), "Workflow has no steps");
110    }
111
112    #[test]
113    fn checkpoint_error_displays_message() {
114        let err = WorkflowError::Checkpoint {
115            message: "write failed".into(),
116        };
117        assert_eq!(err.to_string(), "Checkpoint error: write failed");
118    }
119}