1use thiserror::Error;
4
5#[derive(Error, Debug, Clone, PartialEq)]
7pub enum IronError {
8 #[error("Parse error: {0}")]
10 Parse(String),
11
12 #[error("Security violation: {0}")]
14 SecurityViolation(String),
15
16 #[error("Authentication failed: {0}")]
18 Auth(String),
19
20 #[error("Connection error: {0}")]
22 Connection(String),
23
24 #[error("Protocol violation: {0}")]
26 Protocol(String),
27
28 #[error("Rate limit exceeded: {0}")]
30 RateLimit(String),
31
32 #[error("Configuration error: {0}")]
34 Config(String),
35
36 #[error("Capability error: {0}")]
38 Capability(String),
39
40 #[error("SASL error: {0}")]
42 Sasl(String),
43
44 #[error("I/O error: {0}")]
46 Io(String),
47
48 #[error("Timeout: {0}")]
50 Timeout(String),
51
52 #[error("Invalid input: {0}")]
54 InvalidInput(String),
55
56 #[error("Feature not supported: {0}")]
58 NotSupported(String),
59
60 #[error("Internal error: {0}")]
62 Internal(String),
63}
64
65pub type Result<T> = std::result::Result<T, IronError>;
67
68impl From<std::io::Error> for IronError {
69 fn from(err: std::io::Error) -> Self {
70 IronError::Io(err.to_string())
71 }
72}
73
74#[cfg(feature = "serde")]
75impl From<serde_json::Error> for IronError {
76 fn from(err: serde_json::Error) -> Self {
77 IronError::Parse(format!("JSON parse error: {}", err))
78 }
79}
80
81impl IronError {
82 pub fn is_security_violation(&self) -> bool {
84 matches!(self, IronError::SecurityViolation(_))
85 }
86
87 pub fn is_recoverable(&self) -> bool {
89 match self {
90 IronError::Parse(_) |
91 IronError::Protocol(_) |
92 IronError::RateLimit(_) |
93 IronError::Timeout(_) |
94 IronError::InvalidInput(_) => true,
95
96 IronError::SecurityViolation(_) |
97 IronError::Auth(_) |
98 IronError::Connection(_) |
99 IronError::Config(_) |
100 IronError::Capability(_) |
101 IronError::Sasl(_) |
102 IronError::Io(_) |
103 IronError::NotSupported(_) |
104 IronError::Internal(_) => false,
105 }
106 }
107
108 pub fn category(&self) -> &'static str {
110 match self {
111 IronError::Parse(_) => "parse",
112 IronError::SecurityViolation(_) => "security",
113 IronError::Auth(_) => "auth",
114 IronError::Connection(_) => "connection",
115 IronError::Protocol(_) => "protocol",
116 IronError::RateLimit(_) => "rate_limit",
117 IronError::Config(_) => "config",
118 IronError::Capability(_) => "capability",
119 IronError::Sasl(_) => "sasl",
120 IronError::Io(_) => "io",
121 IronError::Timeout(_) => "timeout",
122 IronError::InvalidInput(_) => "invalid_input",
123 IronError::NotSupported(_) => "not_supported",
124 IronError::Internal(_) => "internal",
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_error_categories() {
135 assert_eq!(IronError::Parse("test".to_string()).category(), "parse");
136 assert_eq!(IronError::SecurityViolation("test".to_string()).category(), "security");
137 assert_eq!(IronError::Auth("test".to_string()).category(), "auth");
138 }
139
140 #[test]
141 fn test_security_violation_detection() {
142 assert!(IronError::SecurityViolation("test".to_string()).is_security_violation());
143 assert!(!IronError::Parse("test".to_string()).is_security_violation());
144 }
145
146 #[test]
147 fn test_recoverable_errors() {
148 assert!(IronError::Parse("test".to_string()).is_recoverable());
149 assert!(IronError::Protocol("test".to_string()).is_recoverable());
150 assert!(!IronError::SecurityViolation("test".to_string()).is_recoverable());
151 assert!(!IronError::Auth("test".to_string()).is_recoverable());
152 }
153
154 #[test]
155 fn test_io_error_conversion() {
156 let io_err = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "test");
157 let iron_err: IronError = io_err.into();
158 assert!(matches!(iron_err, IronError::Io(_)));
159 }
160}