1use thiserror::Error;
2
3#[derive(Error, Debug)]
46pub enum TlqError {
47 #[error("Connection error: {0}")]
52 Connection(String),
53
54 #[error("Timeout error after {0}ms")]
59 Timeout(u64),
60
61 #[error("Server error: {status} - {message}")]
66 Server { status: u16, message: String },
67
68 #[error("Validation error: {0}")]
73 Validation(String),
74
75 #[error("Serialization error: {0}")]
80 Serialization(#[from] serde_json::Error),
81
82 #[error("IO error: {0}")]
87 Io(#[from] std::io::Error),
88
89 #[error("Max retries exceeded ({max_retries}) for operation")]
94 MaxRetriesExceeded { max_retries: u32 },
95
96 #[error("Message too large: {size} bytes (max: 65536)")]
101 MessageTooLarge { size: usize },
102}
103
104impl TlqError {
105 pub fn is_retryable(&self) -> bool {
134 matches!(
135 self,
136 TlqError::Connection(_) | TlqError::Timeout(_) | TlqError::Io(_)
137 )
138 }
139}
140
141pub type Result<T> = std::result::Result<T, TlqError>;
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use std::io::{Error as IoError, ErrorKind};
162
163 #[test]
164 fn test_connection_error_retryable() {
165 let error = TlqError::Connection("Connection refused".to_string());
166 assert!(error.is_retryable());
167
168 let error_msg = format!("{}", error);
169 assert_eq!(error_msg, "Connection error: Connection refused");
170 }
171
172 #[test]
173 fn test_timeout_error_retryable() {
174 let error = TlqError::Timeout(5000);
175 assert!(error.is_retryable());
176
177 let error_msg = format!("{}", error);
178 assert_eq!(error_msg, "Timeout error after 5000ms");
179 }
180
181 #[test]
182 fn test_io_error_retryable() {
183 let io_error = IoError::new(ErrorKind::ConnectionRefused, "Connection refused");
184 let error = TlqError::Io(io_error);
185 assert!(error.is_retryable());
186
187 let error_msg = format!("{}", error);
188 assert!(error_msg.contains("IO error:"));
189 assert!(error_msg.contains("Connection refused"));
190 }
191
192 #[test]
193 fn test_server_error_not_retryable() {
194 let error = TlqError::Server {
195 status: 500,
196 message: "Internal Server Error".to_string(),
197 };
198 assert!(!error.is_retryable());
199
200 let error_msg = format!("{}", error);
201 assert_eq!(error_msg, "Server error: 500 - Internal Server Error");
202 }
203
204 #[test]
205 fn test_validation_error_not_retryable() {
206 let error = TlqError::Validation("Invalid input".to_string());
207 assert!(!error.is_retryable());
208
209 let error_msg = format!("{}", error);
210 assert_eq!(error_msg, "Validation error: Invalid input");
211 }
212
213 #[test]
214 fn test_serialization_error_not_retryable() {
215 let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
217 let error = TlqError::Serialization(json_error);
218 assert!(!error.is_retryable());
219
220 let error_msg = format!("{}", error);
221 assert!(error_msg.contains("Serialization error:"));
222 }
223
224 #[test]
225 fn test_max_retries_exceeded_not_retryable() {
226 let error = TlqError::MaxRetriesExceeded { max_retries: 3 };
227 assert!(!error.is_retryable());
228
229 let error_msg = format!("{}", error);
230 assert_eq!(error_msg, "Max retries exceeded (3) for operation");
231 }
232
233 #[test]
234 fn test_message_too_large_not_retryable() {
235 let error = TlqError::MessageTooLarge { size: 70000 };
236 assert!(!error.is_retryable());
237
238 let error_msg = format!("{}", error);
239 assert_eq!(error_msg, "Message too large: 70000 bytes (max: 65536)");
240 }
241
242 #[test]
243 fn test_error_from_io_error() {
244 let io_error = IoError::new(ErrorKind::PermissionDenied, "Access denied");
245 let tlq_error: TlqError = io_error.into();
246
247 assert!(tlq_error.is_retryable()); assert!(matches!(tlq_error, TlqError::Io(_)));
249 }
250
251 #[test]
252 fn test_error_from_serde_json_error() {
253 let json_error = serde_json::from_str::<serde_json::Value>("{invalid}").unwrap_err();
254 let tlq_error: TlqError = json_error.into();
255
256 assert!(!tlq_error.is_retryable()); assert!(matches!(tlq_error, TlqError::Serialization(_)));
258 }
259
260 #[test]
261 fn test_different_io_error_kinds() {
262 let error_kinds = vec![
263 ErrorKind::NotFound,
264 ErrorKind::PermissionDenied,
265 ErrorKind::ConnectionRefused,
266 ErrorKind::ConnectionReset,
267 ErrorKind::TimedOut,
268 ErrorKind::Interrupted,
269 ];
270
271 for kind in error_kinds {
272 let io_error = IoError::new(kind, format!("{:?} error", kind));
273 let tlq_error = TlqError::Io(io_error);
274
275 assert!(tlq_error.is_retryable());
277 }
278 }
279
280 #[test]
281 fn test_server_error_status_codes() {
282 let test_cases = vec![
283 (400, "Bad Request"),
284 (401, "Unauthorized"),
285 (403, "Forbidden"),
286 (404, "Not Found"),
287 (500, "Internal Server Error"),
288 (502, "Bad Gateway"),
289 (503, "Service Unavailable"),
290 (504, "Gateway Timeout"),
291 ];
292
293 for (status, message) in test_cases {
294 let error = TlqError::Server {
295 status,
296 message: message.to_string(),
297 };
298
299 assert!(!error.is_retryable());
301
302 let error_msg = format!("{}", error);
303 assert!(error_msg.contains(&status.to_string()));
304 assert!(error_msg.contains(message));
305 }
306 }
307
308 #[test]
309 fn test_error_debug_formatting() {
310 let error = TlqError::Connection("test connection error".to_string());
311 let debug_str = format!("{:?}", error);
312 assert!(debug_str.contains("Connection"));
313 assert!(debug_str.contains("test connection error"));
314 }
315
316 #[test]
317 fn test_result_type_alias() {
318 let success: Result<String> = Ok("success".to_string());
320 assert!(success.is_ok());
321 if let Ok(value) = success {
322 assert_eq!(value, "success");
323 }
324
325 let failure: Result<String> = Err(TlqError::Validation("test error".to_string()));
326 assert!(failure.is_err());
327
328 match failure {
329 Err(TlqError::Validation(msg)) => assert_eq!(msg, "test error"),
330 _ => panic!("Expected validation error"),
331 }
332 }
333
334 #[test]
335 fn test_timeout_edge_cases() {
336 let timeout_0 = TlqError::Timeout(0);
338 assert!(timeout_0.is_retryable());
339 assert_eq!(format!("{}", timeout_0), "Timeout error after 0ms");
340
341 let timeout_max = TlqError::Timeout(u64::MAX);
342 assert!(timeout_max.is_retryable());
343 assert_eq!(
344 format!("{}", timeout_max),
345 format!("Timeout error after {}ms", u64::MAX)
346 );
347 }
348
349 #[test]
350 fn test_message_size_edge_cases() {
351 let size_0 = TlqError::MessageTooLarge { size: 0 };
353 assert_eq!(
354 format!("{}", size_0),
355 "Message too large: 0 bytes (max: 65536)"
356 );
357
358 let size_max = TlqError::MessageTooLarge { size: usize::MAX };
359 assert_eq!(
360 format!("{}", size_max),
361 format!("Message too large: {} bytes (max: 65536)", usize::MAX)
362 );
363
364 let size_just_over = TlqError::MessageTooLarge { size: 65537 };
365 assert_eq!(
366 format!("{}", size_just_over),
367 "Message too large: 65537 bytes (max: 65536)"
368 );
369 }
370
371 #[test]
372 fn test_empty_error_messages() {
373 let connection_error = TlqError::Connection("".to_string());
374 assert_eq!(format!("{}", connection_error), "Connection error: ");
375
376 let validation_error = TlqError::Validation("".to_string());
377 assert_eq!(format!("{}", validation_error), "Validation error: ");
378
379 let server_error = TlqError::Server {
380 status: 500,
381 message: "".to_string(),
382 };
383 assert_eq!(format!("{}", server_error), "Server error: 500 - ");
384 }
385}