Skip to main content

aura_core/
errors.rs

1//! Unified error system for Aura core
2//!
3//! This module provides a single, simple error type to replace the over-engineered
4//! error hierarchy. Following the whole system model principle of simplicity.
5
6use serde::{Deserialize, Serialize};
7
8/// Unified error type for all Aura operations
9#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
10pub enum AuraError {
11    /// Invalid input or configuration
12    #[error("Invalid: {message}")]
13    Invalid {
14        /// Error message describing the invalid input
15        message: String,
16    },
17
18    /// Resource not found
19    #[error("Not found: {message}")]
20    NotFound {
21        /// Error message describing what was not found
22        message: String,
23    },
24
25    /// Permission denied
26    #[error("Permission denied: {message}")]
27    PermissionDenied {
28        /// Error message describing the permission issue
29        message: String,
30    },
31
32    /// Cryptographic operation failed
33    #[error("Crypto error: {message}")]
34    Crypto {
35        /// Error message describing the cryptographic failure
36        message: String,
37    },
38
39    /// Network or transport error
40    #[error("Network error: {message}")]
41    Network {
42        /// Error message describing the network issue
43        message: String,
44    },
45
46    /// Serialization/deserialization error
47    #[error("Serialization error: {message}")]
48    Serialization {
49        /// Error message describing the serialization failure
50        message: String,
51    },
52
53    /// Storage operation failed
54    #[error("Storage error: {message}")]
55    Storage {
56        /// Error message describing the storage failure
57        message: String,
58    },
59
60    /// Internal system error
61    #[error("Internal error: {message}")]
62    Internal {
63        /// Error message describing the internal error
64        message: String,
65    },
66
67    /// Terminal operation error
68    #[error("Terminal error: {0}")]
69    Terminal(String),
70}
71
72/// Shared error code mapping for protocol-level errors.
73pub 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    /// Create an invalid input error
95    pub fn invalid(message: impl Into<String>) -> Self {
96        Self::Invalid {
97            message: message.into(),
98        }
99    }
100
101    /// Create a not found error
102    pub fn not_found(message: impl Into<String>) -> Self {
103        Self::NotFound {
104            message: message.into(),
105        }
106    }
107
108    /// Create a permission denied error
109    pub fn permission_denied(message: impl Into<String>) -> Self {
110        Self::PermissionDenied {
111            message: message.into(),
112        }
113    }
114
115    /// Create a crypto error
116    pub fn crypto(message: impl Into<String>) -> Self {
117        Self::Crypto {
118            message: message.into(),
119        }
120    }
121
122    /// Create a network error
123    pub fn network(message: impl Into<String>) -> Self {
124        Self::Network {
125            message: message.into(),
126        }
127    }
128
129    /// Create a serialization error
130    pub fn serialization(message: impl Into<String>) -> Self {
131        Self::Serialization {
132            message: message.into(),
133        }
134    }
135
136    /// Create a storage error
137    pub fn storage(message: impl Into<String>) -> Self {
138        Self::Storage {
139            message: message.into(),
140        }
141    }
142
143    /// Create an agent error
144    pub fn agent(message: impl Into<String>) -> Self {
145        Self::Internal {
146            message: message.into(),
147        }
148    }
149
150    /// Create a chat error
151    pub fn chat(message: impl Into<String>) -> Self {
152        Self::Internal {
153            message: format!("Chat error: {}", message.into()),
154        }
155    }
156
157    /// Create an internal error
158    pub fn internal(message: impl Into<String>) -> Self {
159        Self::Internal {
160            message: message.into(),
161        }
162    }
163
164    /// Create a terminal error
165    pub fn terminal(message: impl Into<String>) -> Self {
166        Self::Terminal(message.into())
167    }
168
169    /// Create a coordination failed error
170    pub fn coordination_failed(message: impl Into<String>) -> Self {
171        Self::Internal {
172            message: format!("Coordination failed: {}", message.into()),
173        }
174    }
175
176    /// Create a budget exceeded error
177    pub fn budget_exceeded(message: impl Into<String>) -> Self {
178        Self::PermissionDenied {
179            message: format!("Budget exceeded: {}", message.into()),
180        }
181    }
182
183    /// Check if this error is retryable
184    pub fn is_retryable(&self) -> bool {
185        match self {
186            // Network errors are typically retryable
187            Self::Network { .. } => true,
188            // Storage errors might be retryable
189            Self::Storage { .. } => true,
190            // Other errors are not retryable
191            _ => false,
192        }
193    }
194
195    /// Get the error category as a string
196    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
211/// Standard Result type for Aura operations
212pub type Result<T> = std::result::Result<T, AuraError>;
213
214// Conversion traits for common error types
215impl 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}