Skip to main content

orcs_app/
error.rs

1//! Application-level error type.
2//!
3//! [`AppError`] unifies all internal errors for the application layer.
4
5use orcs_runtime::{ChannelError, EngineError, StorageError};
6use orcs_types::ErrorCode;
7use thiserror::Error;
8
9/// Unified application error.
10///
11/// Collects all internal errors into a single type for CLI/GUI handling.
12///
13/// # Example
14///
15/// ```
16/// use orcs_app::{AppError, ChannelError};
17/// use orcs_types::ChannelId;
18///
19/// // Internal error automatically converts to AppError
20/// let channel_err = ChannelError::NotFound(ChannelId::new());
21/// let app_err: AppError = channel_err.into();
22///
23/// // CLI can use Display for user-friendly message
24/// eprintln!("Error: {}", app_err);
25/// ```
26#[derive(Debug, Error)]
27pub enum AppError {
28    /// Channel operation failed
29    #[error("Channel error: {0}")]
30    Channel(#[from] ChannelError),
31
32    /// Engine operation failed
33    #[error("Engine error: {0}")]
34    Engine(#[from] EngineError),
35
36    /// Configuration error
37    #[error("Config error: {0}")]
38    Config(String),
39
40    /// IO error
41    #[error("IO error: {0}")]
42    Io(#[from] std::io::Error),
43
44    /// Application not properly initialized
45    #[error("Not initialized: {0}")]
46    NotInitialized(String),
47
48    /// Session storage error
49    #[error("Storage error: {0}")]
50    Storage(#[from] StorageError),
51}
52
53impl ErrorCode for AppError {
54    fn code(&self) -> &'static str {
55        match self {
56            Self::Channel(e) => e.code(),
57            Self::Engine(e) => e.code(),
58            Self::Config(_) => "APP_CONFIG_ERROR",
59            Self::Io(_) => "APP_IO_ERROR",
60            Self::NotInitialized(_) => "APP_NOT_INITIALIZED",
61            Self::Storage(_) => "APP_STORAGE_ERROR",
62        }
63    }
64
65    fn is_recoverable(&self) -> bool {
66        match self {
67            Self::Channel(e) => e.is_recoverable(),
68            Self::Engine(e) => e.is_recoverable(),
69            Self::Config(_) => false,
70            Self::Io(_) => true,
71            Self::NotInitialized(_) => false,
72            Self::Storage(_) => true,
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use orcs_types::ChannelId;
81
82    #[test]
83    fn channel_error_converts() {
84        let err = ChannelError::NotFound(ChannelId::new());
85        let app_err: AppError = err.into();
86        assert!(matches!(app_err, AppError::Channel(_)));
87    }
88
89    #[test]
90    fn error_codes() {
91        let err = AppError::Config("test".into());
92        assert_eq!(err.code(), "APP_CONFIG_ERROR");
93        assert!(!err.is_recoverable());
94    }
95}