1use std::{any::Any, sync::Arc};
18
19use super::transition::Transition;
20
21#[derive(thiserror::Error, Debug)]
23pub enum TaskError {
24 #[error(transparent)]
26 NodeError(#[from] NodeError),
27
28 #[error("MissingTransition: {0}")]
30 MissingTransition(String),
31
32 #[error("MissingNode: {0}")]
34 MissingNode(String),
35
36 #[error("Task failed with wrong output")]
38 TypeError(String),
39
40 #[error("Task already has active or paused work")]
42 TaskActive,
43
44 #[error("Task has no paused or queued work to resume")]
46 NotResumable,
47
48 #[error("Task ended without completing or pausing")]
50 Incomplete,
51
52 #[error("Task entered an invalid internal state: {0}")]
54 InvalidState(String),
55
56 #[error("MissingInput: {0}")]
58 MissingInput(String),
59
60 #[error("MissingOutput: {0}")]
62 MissingOutput(String),
63
64 #[error("Task is missing steps")]
66 NoSteps,
67}
68
69impl TaskError {
70 pub fn missing_transition(node_id: usize) -> Self {
72 TaskError::MissingTransition(format!("Node {node_id} is missing a transition"))
73 }
74
75 pub fn missing_node(node_id: usize) -> Self {
77 TaskError::MissingNode(format!("Node {node_id} is missing"))
78 }
79
80 pub fn missing_input(node_id: usize) -> Self {
82 TaskError::MissingInput(format!("Node {node_id} is missing input"))
83 }
84
85 pub fn missing_output(node_id: usize) -> Self {
87 TaskError::MissingOutput(format!("Node {node_id} is missing output"))
88 }
89
90 pub fn type_error<T: Any + Send>(output: &T) -> Self {
92 let message = format!(
93 "Expected output of type {}, but got {:?}",
94 std::any::type_name::<T>(),
95 output.type_id()
96 );
97 TaskError::TypeError(message)
98 }
99
100 pub fn invalid_state(message: impl Into<String>) -> Self {
102 TaskError::InvalidState(message.into())
103 }
104}
105
106#[derive(Debug, thiserror::Error)]
108pub struct NodeError {
109 pub node_error: Box<dyn std::error::Error + Send + Sync>,
111 pub transition: Option<Arc<Transition>>,
113 pub node_id: usize,
115}
116
117impl std::fmt::Display for NodeError {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(
120 f,
121 "Node error in node {}: {}",
122 self.node_id, self.node_error
123 )
124 }
125}
126
127impl NodeError {
128 pub fn new(
130 node_error: impl Into<Box<dyn std::error::Error + Send + Sync>>,
131 node_id: usize,
132 transition: Option<Transition>,
133 ) -> Self {
134 Self {
135 node_error: node_error.into(),
136 transition: transition.map(Arc::new),
137 node_id,
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::NodeError;
145
146 #[derive(Debug)]
147 struct ByteBackedError(Vec<u8>);
148
149 impl std::fmt::Display for ByteBackedError {
150 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 formatter.write_str(&String::from_utf8_lossy(&self.0))
152 }
153 }
154
155 impl std::error::Error for ByteBackedError {}
156
157 #[test]
158 fn node_error_uses_inner_error_display() {
159 let error = NodeError::new(ByteBackedError(b"readable failure".to_vec()), 2, None);
160
161 assert_eq!(error.to_string(), "Node error in node 2: readable failure");
162 }
163}