1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum TlqError {
5 #[error("Connection error: {0}")]
6 Connection(String),
7
8 #[error("Timeout error after {0}ms")]
9 Timeout(u64),
10
11 #[error("Server error: {status} - {message}")]
12 Server { status: u16, message: String },
13
14 #[error("Validation error: {0}")]
15 Validation(String),
16
17 #[error("Serialization error: {0}")]
18 Serialization(#[from] serde_json::Error),
19
20 #[error("IO error: {0}")]
21 Io(#[from] std::io::Error),
22
23 #[error("Max retries exceeded ({max_retries}) for operation")]
24 MaxRetriesExceeded { max_retries: u32 },
25
26 #[error("Message too large: {size} bytes (max: 65536)")]
27 MessageTooLarge { size: usize },
28}
29
30impl TlqError {
31 pub fn is_retryable(&self) -> bool {
32 matches!(
33 self,
34 TlqError::Connection(_) | TlqError::Timeout(_) | TlqError::Io(_)
35 )
36 }
37}
38
39pub type Result<T> = std::result::Result<T, TlqError>;
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use std::io::{Error as IoError, ErrorKind};
45
46 #[test]
47 fn test_connection_error_retryable() {
48 let error = TlqError::Connection("Connection refused".to_string());
49 assert!(error.is_retryable());
50
51 let error_msg = format!("{}", error);
52 assert_eq!(error_msg, "Connection error: Connection refused");
53 }
54
55 #[test]
56 fn test_timeout_error_retryable() {
57 let error = TlqError::Timeout(5000);
58 assert!(error.is_retryable());
59
60 let error_msg = format!("{}", error);
61 assert_eq!(error_msg, "Timeout error after 5000ms");
62 }
63
64 #[test]
65 fn test_io_error_retryable() {
66 let io_error = IoError::new(ErrorKind::ConnectionRefused, "Connection refused");
67 let error = TlqError::Io(io_error);
68 assert!(error.is_retryable());
69
70 let error_msg = format!("{}", error);
71 assert!(error_msg.contains("IO error:"));
72 assert!(error_msg.contains("Connection refused"));
73 }
74
75 #[test]
76 fn test_server_error_not_retryable() {
77 let error = TlqError::Server {
78 status: 500,
79 message: "Internal Server Error".to_string(),
80 };
81 assert!(!error.is_retryable());
82
83 let error_msg = format!("{}", error);
84 assert_eq!(error_msg, "Server error: 500 - Internal Server Error");
85 }
86
87 #[test]
88 fn test_validation_error_not_retryable() {
89 let error = TlqError::Validation("Invalid input".to_string());
90 assert!(!error.is_retryable());
91
92 let error_msg = format!("{}", error);
93 assert_eq!(error_msg, "Validation error: Invalid input");
94 }
95
96 #[test]
97 fn test_serialization_error_not_retryable() {
98 let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
100 let error = TlqError::Serialization(json_error);
101 assert!(!error.is_retryable());
102
103 let error_msg = format!("{}", error);
104 assert!(error_msg.contains("Serialization error:"));
105 }
106
107 #[test]
108 fn test_max_retries_exceeded_not_retryable() {
109 let error = TlqError::MaxRetriesExceeded { max_retries: 3 };
110 assert!(!error.is_retryable());
111
112 let error_msg = format!("{}", error);
113 assert_eq!(error_msg, "Max retries exceeded (3) for operation");
114 }
115
116 #[test]
117 fn test_message_too_large_not_retryable() {
118 let error = TlqError::MessageTooLarge { size: 70000 };
119 assert!(!error.is_retryable());
120
121 let error_msg = format!("{}", error);
122 assert_eq!(error_msg, "Message too large: 70000 bytes (max: 65536)");
123 }
124
125 #[test]
126 fn test_error_from_io_error() {
127 let io_error = IoError::new(ErrorKind::PermissionDenied, "Access denied");
128 let tlq_error: TlqError = io_error.into();
129
130 assert!(tlq_error.is_retryable()); assert!(matches!(tlq_error, TlqError::Io(_)));
132 }
133
134 #[test]
135 fn test_error_from_serde_json_error() {
136 let json_error = serde_json::from_str::<serde_json::Value>("{invalid}").unwrap_err();
137 let tlq_error: TlqError = json_error.into();
138
139 assert!(!tlq_error.is_retryable()); assert!(matches!(tlq_error, TlqError::Serialization(_)));
141 }
142
143 #[test]
144 fn test_different_io_error_kinds() {
145 let error_kinds = vec![
146 ErrorKind::NotFound,
147 ErrorKind::PermissionDenied,
148 ErrorKind::ConnectionRefused,
149 ErrorKind::ConnectionReset,
150 ErrorKind::TimedOut,
151 ErrorKind::Interrupted,
152 ];
153
154 for kind in error_kinds {
155 let io_error = IoError::new(kind, format!("{:?} error", kind));
156 let tlq_error = TlqError::Io(io_error);
157
158 assert!(tlq_error.is_retryable());
160 }
161 }
162
163 #[test]
164 fn test_server_error_status_codes() {
165 let test_cases = vec![
166 (400, "Bad Request"),
167 (401, "Unauthorized"),
168 (403, "Forbidden"),
169 (404, "Not Found"),
170 (500, "Internal Server Error"),
171 (502, "Bad Gateway"),
172 (503, "Service Unavailable"),
173 (504, "Gateway Timeout"),
174 ];
175
176 for (status, message) in test_cases {
177 let error = TlqError::Server {
178 status,
179 message: message.to_string(),
180 };
181
182 assert!(!error.is_retryable());
184
185 let error_msg = format!("{}", error);
186 assert!(error_msg.contains(&status.to_string()));
187 assert!(error_msg.contains(message));
188 }
189 }
190
191 #[test]
192 fn test_error_debug_formatting() {
193 let error = TlqError::Connection("test connection error".to_string());
194 let debug_str = format!("{:?}", error);
195 assert!(debug_str.contains("Connection"));
196 assert!(debug_str.contains("test connection error"));
197 }
198
199 #[test]
200 fn test_result_type_alias() {
201 let success: Result<String> = Ok("success".to_string());
203 assert!(success.is_ok());
204 if let Ok(value) = success {
205 assert_eq!(value, "success");
206 }
207
208 let failure: Result<String> = Err(TlqError::Validation("test error".to_string()));
209 assert!(failure.is_err());
210
211 match failure {
212 Err(TlqError::Validation(msg)) => assert_eq!(msg, "test error"),
213 _ => panic!("Expected validation error"),
214 }
215 }
216
217 #[test]
218 fn test_timeout_edge_cases() {
219 let timeout_0 = TlqError::Timeout(0);
221 assert!(timeout_0.is_retryable());
222 assert_eq!(format!("{}", timeout_0), "Timeout error after 0ms");
223
224 let timeout_max = TlqError::Timeout(u64::MAX);
225 assert!(timeout_max.is_retryable());
226 assert_eq!(
227 format!("{}", timeout_max),
228 format!("Timeout error after {}ms", u64::MAX)
229 );
230 }
231
232 #[test]
233 fn test_message_size_edge_cases() {
234 let size_0 = TlqError::MessageTooLarge { size: 0 };
236 assert_eq!(
237 format!("{}", size_0),
238 "Message too large: 0 bytes (max: 65536)"
239 );
240
241 let size_max = TlqError::MessageTooLarge { size: usize::MAX };
242 assert_eq!(
243 format!("{}", size_max),
244 format!("Message too large: {} bytes (max: 65536)", usize::MAX)
245 );
246
247 let size_just_over = TlqError::MessageTooLarge { size: 65537 };
248 assert_eq!(
249 format!("{}", size_just_over),
250 "Message too large: 65537 bytes (max: 65536)"
251 );
252 }
253
254 #[test]
255 fn test_empty_error_messages() {
256 let connection_error = TlqError::Connection("".to_string());
257 assert_eq!(format!("{}", connection_error), "Connection error: ");
258
259 let validation_error = TlqError::Validation("".to_string());
260 assert_eq!(format!("{}", validation_error), "Validation error: ");
261
262 let server_error = TlqError::Server {
263 status: 500,
264 message: "".to_string(),
265 };
266 assert_eq!(format!("{}", server_error), "Server error: 500 - ");
267 }
268}