1use std::fmt;
4
5pub type Result<T> = std::result::Result<T, OdinError>;
7
8#[derive(Debug, Clone)]
10pub enum OdinError {
11 Configuration(String),
13 Network(String),
15 Protocol(String),
17 Rule(String),
19 Message(String),
21 Serialization(String),
23 Authentication(String),
25 Timeout(String),
27 Connection(String),
29 InvalidState(String),
31 NotFound(String),
33 PermissionDenied(String),
35 RateLimitExceeded(String),
37 Internal(String),
39}
40
41impl fmt::Display for OdinError {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 match self {
44 OdinError::Configuration(msg) => write!(f, "Configuration error: {}", msg),
45 OdinError::Network(msg) => write!(f, "Network error: {}", msg),
46 OdinError::Protocol(msg) => write!(f, "Protocol error: {}", msg),
47 OdinError::Rule(msg) => write!(f, "Rule error: {}", msg),
48 OdinError::Message(msg) => write!(f, "Message error: {}", msg),
49 OdinError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
50 OdinError::Authentication(msg) => write!(f, "Authentication error: {}", msg),
51 OdinError::Timeout(msg) => write!(f, "Timeout error: {}", msg),
52 OdinError::Connection(msg) => write!(f, "Connection error: {}", msg),
53 OdinError::InvalidState(msg) => write!(f, "Invalid state error: {}", msg),
54 OdinError::NotFound(msg) => write!(f, "Not found error: {}", msg),
55 OdinError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
56 OdinError::RateLimitExceeded(msg) => write!(f, "Rate limit exceeded: {}", msg),
57 OdinError::Internal(msg) => write!(f, "Internal error: {}", msg),
58 }
59 }
60}
61
62impl std::error::Error for OdinError {}
63
64impl From<serde_json::Error> for OdinError {
65 fn from(err: serde_json::Error) -> Self {
66 OdinError::Serialization(err.to_string())
67 }
68}
69
70impl From<tokio::time::error::Elapsed> for OdinError {
71 fn from(err: tokio::time::error::Elapsed) -> Self {
72 OdinError::Timeout(err.to_string())
73 }
74}
75
76impl From<std::io::Error> for OdinError {
77 fn from(err: std::io::Error) -> Self {
78 match err.kind() {
79 std::io::ErrorKind::TimedOut => OdinError::Timeout(err.to_string()),
80 std::io::ErrorKind::ConnectionRefused |
81 std::io::ErrorKind::ConnectionAborted |
82 std::io::ErrorKind::ConnectionReset => OdinError::Connection(err.to_string()),
83 std::io::ErrorKind::NotFound => OdinError::NotFound(err.to_string()),
84 std::io::ErrorKind::PermissionDenied => OdinError::PermissionDenied(err.to_string()),
85 _ => OdinError::Internal(err.to_string()),
86 }
87 }
88}
89
90impl OdinError {
92 pub fn config<S: Into<String>>(msg: S) -> Self {
94 OdinError::Configuration(msg.into())
95 }
96
97 pub fn network<S: Into<String>>(msg: S) -> Self {
99 OdinError::Network(msg.into())
100 }
101
102 pub fn protocol<S: Into<String>>(msg: S) -> Self {
104 OdinError::Protocol(msg.into())
105 }
106
107 pub fn rule<S: Into<String>>(msg: S) -> Self {
109 OdinError::Rule(msg.into())
110 }
111
112 pub fn message<S: Into<String>>(msg: S) -> Self {
114 OdinError::Message(msg.into())
115 }
116
117 pub fn timeout<S: Into<String>>(msg: S) -> Self {
119 OdinError::Timeout(msg.into())
120 }
121
122 pub fn internal<S: Into<String>>(msg: S) -> Self {
124 OdinError::Internal(msg.into())
125 }
126
127 pub fn is_retryable(&self) -> bool {
129 matches!(
130 self,
131 OdinError::Network(_) |
132 OdinError::Timeout(_) |
133 OdinError::Connection(_) |
134 OdinError::RateLimitExceeded(_)
135 )
136 }
137
138 pub fn category(&self) -> ErrorCategory {
140 match self {
141 OdinError::Configuration(_) => ErrorCategory::Configuration,
142 OdinError::Network(_) | OdinError::Connection(_) => ErrorCategory::Network,
143 OdinError::Protocol(_) => ErrorCategory::Protocol,
144 OdinError::Rule(_) => ErrorCategory::Rule,
145 OdinError::Message(_) => ErrorCategory::Message,
146 OdinError::Serialization(_) => ErrorCategory::Serialization,
147 OdinError::Authentication(_) | OdinError::PermissionDenied(_) => ErrorCategory::Security,
148 OdinError::Timeout(_) => ErrorCategory::Timeout,
149 OdinError::InvalidState(_) => ErrorCategory::State,
150 OdinError::NotFound(_) => ErrorCategory::NotFound,
151 OdinError::RateLimitExceeded(_) => ErrorCategory::RateLimit,
152 OdinError::Internal(_) => ErrorCategory::Internal,
153 }
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum ErrorCategory {
160 Configuration,
161 Network,
162 Protocol,
163 Rule,
164 Message,
165 Serialization,
166 Security,
167 Timeout,
168 State,
169 NotFound,
170 RateLimit,
171 Internal,
172}
173
174impl fmt::Display for ErrorCategory {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 match self {
177 ErrorCategory::Configuration => write!(f, "configuration"),
178 ErrorCategory::Network => write!(f, "network"),
179 ErrorCategory::Protocol => write!(f, "protocol"),
180 ErrorCategory::Rule => write!(f, "rule"),
181 ErrorCategory::Message => write!(f, "message"),
182 ErrorCategory::Serialization => write!(f, "serialization"),
183 ErrorCategory::Security => write!(f, "security"),
184 ErrorCategory::Timeout => write!(f, "timeout"),
185 ErrorCategory::State => write!(f, "state"),
186 ErrorCategory::NotFound => write!(f, "not_found"),
187 ErrorCategory::RateLimit => write!(f, "rate_limit"),
188 ErrorCategory::Internal => write!(f, "internal"),
189 }
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_error_display() {
199 let error = OdinError::Configuration("Invalid config".to_string());
200 assert_eq!(error.to_string(), "Configuration error: Invalid config");
201 }
202
203 #[test]
204 fn test_error_category() {
205 let error = OdinError::Network("Connection failed".to_string());
206 assert_eq!(error.category(), ErrorCategory::Network);
207 }
208
209 #[test]
210 fn test_error_retryable() {
211 let network_error = OdinError::Network("Temporary failure".to_string());
212 assert!(network_error.is_retryable());
213
214 let config_error = OdinError::Configuration("Invalid setting".to_string());
215 assert!(!config_error.is_retryable());
216 }
217
218 #[test]
219 fn test_error_constructors() {
220 let error = OdinError::config("test");
221 assert!(matches!(error, OdinError::Configuration(_)));
222
223 let error = OdinError::network("test");
224 assert!(matches!(error, OdinError::Network(_)));
225
226 let error = OdinError::timeout("test");
227 assert!(matches!(error, OdinError::Timeout(_)));
228 }
229
230 #[test]
231 fn test_from_serde_error() {
232 let json_error = serde_json::from_str::<i32>("invalid json").unwrap_err();
233 let odin_error: OdinError = json_error.into();
234 assert!(matches!(odin_error, OdinError::Serialization(_)));
235 }
236}