1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
10pub enum AuraError {
11 #[error("Invalid: {message}")]
13 Invalid {
14 message: String,
16 },
17
18 #[error("Not found: {message}")]
20 NotFound {
21 message: String,
23 },
24
25 #[error("Permission denied: {message}")]
27 PermissionDenied {
28 message: String,
30 },
31
32 #[error("Crypto error: {message}")]
34 Crypto {
35 message: String,
37 },
38
39 #[error("Network error: {message}")]
41 Network {
42 message: String,
44 },
45
46 #[error("Serialization error: {message}")]
48 Serialization {
49 message: String,
51 },
52
53 #[error("Storage error: {message}")]
55 Storage {
56 message: String,
58 },
59
60 #[error("Internal error: {message}")]
62 Internal {
63 message: String,
65 },
66
67 #[error("Terminal error: {0}")]
69 Terminal(String),
70}
71
72pub trait ProtocolErrorCode {
74 fn code(&self) -> &'static str;
75}
76
77impl ProtocolErrorCode for AuraError {
78 fn code(&self) -> &'static str {
79 match self {
80 AuraError::Invalid { .. } => "invalid",
81 AuraError::NotFound { .. } => "not_found",
82 AuraError::PermissionDenied { .. } => "permission_denied",
83 AuraError::Crypto { .. } => "crypto",
84 AuraError::Network { .. } => "network",
85 AuraError::Serialization { .. } => "serialization",
86 AuraError::Storage { .. } => "storage",
87 AuraError::Internal { .. } => "internal",
88 AuraError::Terminal(_) => "terminal",
89 }
90 }
91}
92
93impl AuraError {
94 pub fn invalid(message: impl Into<String>) -> Self {
96 Self::Invalid {
97 message: message.into(),
98 }
99 }
100
101 pub fn not_found(message: impl Into<String>) -> Self {
103 Self::NotFound {
104 message: message.into(),
105 }
106 }
107
108 pub fn permission_denied(message: impl Into<String>) -> Self {
110 Self::PermissionDenied {
111 message: message.into(),
112 }
113 }
114
115 pub fn crypto(message: impl Into<String>) -> Self {
117 Self::Crypto {
118 message: message.into(),
119 }
120 }
121
122 pub fn network(message: impl Into<String>) -> Self {
124 Self::Network {
125 message: message.into(),
126 }
127 }
128
129 pub fn serialization(message: impl Into<String>) -> Self {
131 Self::Serialization {
132 message: message.into(),
133 }
134 }
135
136 pub fn storage(message: impl Into<String>) -> Self {
138 Self::Storage {
139 message: message.into(),
140 }
141 }
142
143 pub fn agent(message: impl Into<String>) -> Self {
145 Self::Internal {
146 message: message.into(),
147 }
148 }
149
150 pub fn chat(message: impl Into<String>) -> Self {
152 Self::Internal {
153 message: format!("Chat error: {}", message.into()),
154 }
155 }
156
157 pub fn internal(message: impl Into<String>) -> Self {
159 Self::Internal {
160 message: message.into(),
161 }
162 }
163
164 pub fn terminal(message: impl Into<String>) -> Self {
166 Self::Terminal(message.into())
167 }
168
169 pub fn coordination_failed(message: impl Into<String>) -> Self {
171 Self::Internal {
172 message: format!("Coordination failed: {}", message.into()),
173 }
174 }
175
176 pub fn budget_exceeded(message: impl Into<String>) -> Self {
178 Self::PermissionDenied {
179 message: format!("Budget exceeded: {}", message.into()),
180 }
181 }
182
183 pub fn is_retryable(&self) -> bool {
185 match self {
186 Self::Network { .. } => true,
188 Self::Storage { .. } => true,
190 _ => false,
192 }
193 }
194
195 pub fn category(&self) -> &'static str {
197 match self {
198 Self::Invalid { .. } => "invalid",
199 Self::NotFound { .. } => "not_found",
200 Self::PermissionDenied { .. } => "permission_denied",
201 Self::Crypto { .. } => "crypto",
202 Self::Network { .. } => "network",
203 Self::Serialization { .. } => "serialization",
204 Self::Storage { .. } => "storage",
205 Self::Internal { .. } => "internal",
206 Self::Terminal(_) => "terminal",
207 }
208 }
209}
210
211pub type Result<T> = std::result::Result<T, AuraError>;
213
214impl From<serde_json::Error> for AuraError {
216 fn from(err: serde_json::Error) -> Self {
217 Self::serialization(err.to_string())
218 }
219}
220
221impl From<toml::de::Error> for AuraError {
222 fn from(err: toml::de::Error) -> Self {
223 Self::serialization(err.to_string())
224 }
225}
226
227impl From<std::io::Error> for AuraError {
228 fn from(err: std::io::Error) -> Self {
229 match err.kind() {
230 std::io::ErrorKind::NotFound => Self::not_found(err.to_string()),
231 std::io::ErrorKind::PermissionDenied => Self::permission_denied(err.to_string()),
232 _ => Self::internal(err.to_string()),
233 }
234 }
235}
236
237impl From<Box<dyn std::error::Error + Send + Sync>> for AuraError {
238 fn from(err: Box<dyn std::error::Error + Send + Sync>) -> Self {
239 Self::internal(err.to_string())
240 }
241}
242
243impl From<biscuit_auth::error::Token> for AuraError {
244 fn from(err: biscuit_auth::error::Token) -> Self {
245 Self::permission_denied(format!("Biscuit token error: {err}"))
246 }
247}
248
249impl From<uuid::Error> for AuraError {
250 fn from(err: uuid::Error) -> Self {
251 Self::invalid(format!("UUID error: {err}"))
252 }
253}
254
255impl From<hex::FromHexError> for AuraError {
256 fn from(err: hex::FromHexError) -> Self {
257 Self::serialization(format!("Hex decoding error: {err}"))
258 }
259}
260
261impl From<base64::DecodeError> for AuraError {
262 fn from(err: base64::DecodeError) -> Self {
263 Self::serialization(format!("Base64 decoding error: {err}"))
264 }
265}
266
267impl From<crate::effects::StorageError> for AuraError {
268 fn from(err: crate::effects::StorageError) -> Self {
269 Self::storage(format!("Storage error: {err}"))
270 }
271}
272
273impl From<crate::effects::TimeError> for AuraError {
274 fn from(err: crate::effects::TimeError) -> Self {
275 Self::internal(format!("Time error: {err}"))
276 }
277}
278
279impl From<crate::effects::NetworkError> for AuraError {
280 fn from(err: crate::effects::NetworkError) -> Self {
281 Self::network(format!("{err}"))
282 }
283}
284
285impl From<crate::effects::ChoreographyError> for AuraError {
286 fn from(err: crate::effects::ChoreographyError) -> Self {
287 Self::internal(format!("Choreography error: {err}"))
288 }
289}
290
291impl From<crate::effects::AuthorizationError> for AuraError {
292 fn from(err: crate::effects::AuthorizationError) -> Self {
293 Self::permission_denied(format!("{err}"))
294 }
295}
296
297impl From<crate::effects::QueryError> for AuraError {
298 fn from(err: crate::effects::QueryError) -> Self {
299 Self::invalid(format!("Query error: {err}"))
300 }
301}
302
303impl From<crate::effects::FactError> for AuraError {
304 fn from(err: crate::effects::FactError) -> Self {
305 Self::invalid(format!("Fact error: {err}"))
306 }
307}
308
309impl From<crate::util::serialization::SerializationError> for AuraError {
310 fn from(err: crate::util::serialization::SerializationError) -> Self {
311 Self::serialization(format!("{err}"))
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_error_creation() {
321 let err = AuraError::invalid("test message");
322 assert!(matches!(err, AuraError::Invalid { .. }));
323 assert_eq!(err.to_string(), "Invalid: test message");
324 }
325
326 #[test]
327 fn test_error_conversion() {
328 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
329 let aura_err = AuraError::from(io_err);
330 assert!(matches!(aura_err, AuraError::NotFound { .. }));
331 }
332
333 #[test]
334 fn test_result_type() {
335 fn test_function() -> Result<i32> {
336 Ok(42)
337 }
338
339 let result = test_function();
340 assert!(result.is_ok());
341 assert_eq!(result.unwrap(), 42);
342 }
343}