dapr_durabletask/api/
errors.rs1use super::FailureDetails;
2
3#[derive(Debug, thiserror::Error)]
5pub enum DurableTaskError {
6 #[error("gRPC error: {0}")]
7 GrpcError(Box<tonic::Status>),
8
9 #[error("Orchestration '{instance_id}' failed: {message}")]
10 OrchestrationFailed {
11 instance_id: String,
12 message: String,
13 failure_details: Option<FailureDetails>,
14 },
15
16 #[error("Orchestration '{instance_id}' not found")]
17 InstanceNotFound { instance_id: String },
18
19 #[error("Task failed: {message}")]
20 TaskFailed {
21 message: String,
22 failure_details: Option<FailureDetails>,
23 },
24
25 #[error("Non-determinism error: {message}")]
26 NonDeterminism { message: String },
27
28 #[error("Orchestration state error: {message}")]
29 OrchestrationState { message: String },
30
31 #[error("Timeout waiting for orchestration")]
32 Timeout,
33
34 #[error("Serialisation error: {0}")]
35 Serialization(#[from] serde_json::Error),
36
37 #[error("Invalid address: {0}")]
39 InvalidAddress(String),
40
41 #[error("Connection failed: {0}")]
43 ConnectionFailed(String),
44
45 #[error("Internal error: {0}")]
47 Internal(String),
48
49 #[error("{0}")]
50 Other(String),
51}
52
53impl From<tonic::Status> for DurableTaskError {
54 fn from(status: tonic::Status) -> Self {
55 DurableTaskError::GrpcError(Box::new(status))
56 }
57}
58
59pub type Result<T> = std::result::Result<T, DurableTaskError>;
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn display_grpc_error() {
68 let err = DurableTaskError::GrpcError(Box::new(tonic::Status::internal("oops")));
69 let msg = err.to_string();
70 assert!(msg.starts_with("gRPC error: "));
71 assert!(msg.contains("oops"));
72 }
73
74 #[test]
75 fn display_orchestration_failed() {
76 let err = DurableTaskError::OrchestrationFailed {
77 instance_id: "abc".into(),
78 message: "boom".into(),
79 failure_details: None,
80 };
81 assert_eq!(err.to_string(), "Orchestration 'abc' failed: boom");
82 }
83
84 #[test]
85 fn display_instance_not_found() {
86 let err = DurableTaskError::InstanceNotFound {
87 instance_id: "xyz".into(),
88 };
89 assert_eq!(err.to_string(), "Orchestration 'xyz' not found");
90 }
91
92 #[test]
93 fn display_task_failed() {
94 let err = DurableTaskError::TaskFailed {
95 message: "task err".into(),
96 failure_details: None,
97 };
98 assert_eq!(err.to_string(), "Task failed: task err");
99 }
100
101 #[test]
102 fn display_timeout() {
103 assert_eq!(
104 DurableTaskError::Timeout.to_string(),
105 "Timeout waiting for orchestration"
106 );
107 }
108
109 #[test]
110 fn display_other() {
111 let err = DurableTaskError::Other("custom".into());
112 assert_eq!(err.to_string(), "custom");
113 }
114
115 #[test]
116 fn display_invalid_address() {
117 let err = DurableTaskError::InvalidAddress("not a url".into());
118 assert_eq!(err.to_string(), "Invalid address: not a url");
119 }
120
121 #[test]
122 fn display_connection_failed() {
123 let err = DurableTaskError::ConnectionFailed("refused".into());
124 assert_eq!(err.to_string(), "Connection failed: refused");
125 }
126
127 #[test]
128 fn display_internal() {
129 let err = DurableTaskError::Internal("semaphore closed".into());
130 assert_eq!(err.to_string(), "Internal error: semaphore closed");
131 }
132
133 #[test]
134 fn from_tonic_status() {
135 let status = tonic::Status::internal("test");
136 let err: DurableTaskError = status.into();
137 assert!(matches!(err, DurableTaskError::GrpcError(_)));
138 }
139
140 #[test]
141 fn from_serde_json_error() {
142 let json_err = serde_json::from_str::<String>("not valid json").unwrap_err();
143 let err: DurableTaskError = json_err.into();
144 assert!(matches!(err, DurableTaskError::Serialization(_)));
145 }
146}