Skip to main content

rc_core/
error.rs

1//! Error types for rc-core
2//!
3//! Provides a unified error type that can be converted to appropriate exit codes.
4
5use thiserror::Error;
6
7/// Result type alias for rc-core operations
8pub type Result<T> = std::result::Result<T, Error>;
9
10/// Error types for rc-core operations
11#[derive(Error, Debug)]
12pub enum Error {
13    /// Configuration file error
14    #[error("Configuration error: {0}")]
15    Config(String),
16
17    /// Invalid path format
18    #[error("Invalid path: {0}")]
19    InvalidPath(String),
20
21    /// Alias not found
22    #[error("Alias not found: {0}")]
23    AliasNotFound(String),
24
25    /// Alias already exists
26    #[error("Alias already exists: {0}")]
27    AliasExists(String),
28
29    /// IO error
30    #[error("IO error: {0}")]
31    Io(#[from] std::io::Error),
32
33    /// TOML parsing error
34    #[error("TOML parse error: {0}")]
35    TomlParse(#[from] toml::de::Error),
36
37    /// TOML serialization error
38    #[error("TOML serialization error: {0}")]
39    TomlSerialize(#[from] toml::ser::Error),
40
41    /// JSON error
42    #[error("JSON error: {0}")]
43    Json(#[from] serde_json::Error),
44
45    /// URL parsing error
46    #[error("Invalid URL: {0}")]
47    InvalidUrl(#[from] url::ParseError),
48
49    /// Authentication error
50    #[error("Authentication failed: {0}")]
51    Auth(String),
52
53    /// Resource not found
54    #[error("Not found: {0}")]
55    NotFound(String),
56
57    /// Network error (retryable)
58    #[error("Network error: {0}")]
59    Network(String),
60
61    /// Conflict error
62    #[error("Conflict: {0}")]
63    Conflict(String),
64
65    /// Feature not supported by backend
66    #[error("Unsupported feature: {0}")]
67    UnsupportedFeature(String),
68
69    /// General error
70    #[error("{0}")]
71    General(String),
72}
73
74impl Error {
75    /// Get the appropriate exit code for this error
76    pub const fn exit_code(&self) -> i32 {
77        match self {
78            Error::InvalidPath(_) => 2,                        // UsageError
79            Error::Config(_) => 2,                             // UsageError
80            Error::Network(_) => 3,                            // NetworkError
81            Error::Auth(_) => 4,                               // AuthError
82            Error::NotFound(_) | Error::AliasNotFound(_) => 5, // NotFound
83            Error::Conflict(_) | Error::AliasExists(_) => 6,   // Conflict
84            Error::UnsupportedFeature(_) => 7,                 // UnsupportedFeature
85            _ => 1,                                            // GeneralError
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_error_exit_codes() {
96        assert_eq!(Error::InvalidPath("test".into()).exit_code(), 2);
97        assert_eq!(Error::Config("test".into()).exit_code(), 2);
98        assert_eq!(Error::Network("test".into()).exit_code(), 3);
99        assert_eq!(Error::Auth("test".into()).exit_code(), 4);
100        assert_eq!(Error::NotFound("test".into()).exit_code(), 5);
101        assert_eq!(Error::AliasNotFound("test".into()).exit_code(), 5);
102        assert_eq!(Error::Conflict("test".into()).exit_code(), 6);
103        assert_eq!(Error::AliasExists("test".into()).exit_code(), 6);
104        assert_eq!(Error::UnsupportedFeature("test".into()).exit_code(), 7);
105        assert_eq!(Error::General("test".into()).exit_code(), 1);
106    }
107
108    #[test]
109    fn test_error_display() {
110        let err = Error::AliasNotFound("myalias".into());
111        assert_eq!(err.to_string(), "Alias not found: myalias");
112
113        let err = Error::InvalidPath("/bad/path".into());
114        assert_eq!(err.to_string(), "Invalid path: /bad/path");
115    }
116}