rust_rabbit/
error.rs

1//! Simplified Error Handling for rust-rabbit
2//!
3//! Basic error types for core RabbitMQ operations without complex pattern-specific errors.
4
5use thiserror::Error;
6
7/// Main error type for rust-rabbit library
8#[derive(Error, Debug)]
9pub enum RustRabbitError {
10    #[error("Connection error: {0}")]
11    Connection(String),
12
13    #[error("RabbitMQ protocol error: {0}")]
14    Protocol(#[from] lapin::Error),
15
16    #[error("Serialization error: {0}")]
17    Serialization(String),
18
19    #[error("Configuration error: {0}")]
20    Configuration(String),
21
22    #[error("Consumer error: {0}")]
23    Consumer(String),
24
25    #[error("Publisher error: {0}")]
26    Publisher(String),
27
28    #[error("Retry error: {0}")]
29    Retry(String),
30
31    #[error("IO error: {0}")]
32    Io(#[from] std::io::Error),
33}
34
35/// Result type alias for convenience
36pub type Result<T> = std::result::Result<T, RustRabbitError>;
37
38impl RustRabbitError {
39    /// Check if the error is retryable
40    pub fn is_retryable(&self) -> bool {
41        match self {
42            // Connection errors are usually retryable
43            RustRabbitError::Connection(_) => true,
44            RustRabbitError::Protocol(lapin_error) => {
45                // Check if it's a temporary protocol error using lapin's methods
46                // In lapin 3.7+, Error is a struct with kind() method
47                use lapin::ErrorKind;
48                matches!(
49                    lapin_error.kind(),
50                    ErrorKind::IOError(_) | ErrorKind::ProtocolError(_)
51                )
52            }
53            // Serialization and configuration errors are not retryable
54            RustRabbitError::Serialization(_) => false,
55            RustRabbitError::Configuration(_) => false,
56            // Consumer/Publisher errors might be retryable depending on context
57            RustRabbitError::Consumer(_) => true,
58            RustRabbitError::Publisher(_) => true,
59            // Retry errors are not retryable (to avoid infinite loops)
60            RustRabbitError::Retry(_) => false,
61            // IO errors might be retryable
62            RustRabbitError::Io(_) => true,
63        }
64    }
65
66    /// Check if the error indicates a connection issue
67    pub fn is_connection_error(&self) -> bool {
68        matches!(
69            self,
70            RustRabbitError::Connection(_) | RustRabbitError::Protocol(_) | RustRabbitError::Io(_)
71        )
72    }
73
74    /// Get a user-friendly error message
75    pub fn user_message(&self) -> String {
76        match self {
77            RustRabbitError::Connection(_) => {
78                "Failed to connect to RabbitMQ. Please check your connection settings.".to_string()
79            }
80            RustRabbitError::Protocol(_) => {
81                "RabbitMQ protocol error. The connection might be unstable.".to_string()
82            }
83            RustRabbitError::Serialization(_) => {
84                "Failed to serialize/deserialize message. Please check your message format."
85                    .to_string()
86            }
87            RustRabbitError::Configuration(_) => {
88                "Configuration error. Please check your settings.".to_string()
89            }
90            RustRabbitError::Consumer(_) => {
91                "Consumer error. Failed to process message.".to_string()
92            }
93            RustRabbitError::Publisher(_) => "Publisher error. Failed to send message.".to_string(),
94            RustRabbitError::Retry(_) => {
95                "Retry mechanism failed. Message processing could not be completed.".to_string()
96            }
97            RustRabbitError::Io(_) => {
98                "IO error occurred. This might be a temporary issue.".to_string()
99            }
100        }
101    }
102}
103
104/// Convert serde_json errors to RustRabbitError
105impl From<serde_json::Error> for RustRabbitError {
106    fn from(err: serde_json::Error) -> Self {
107        RustRabbitError::Serialization(err.to_string())
108    }
109}
110
111/// Convert URL parsing errors to RustRabbitError
112impl From<url::ParseError> for RustRabbitError {
113    fn from(err: url::ParseError) -> Self {
114        RustRabbitError::Configuration(format!("Invalid URL: {}", err))
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_error_retryable() {
124        let conn_error = RustRabbitError::Connection("test".to_string());
125        assert!(conn_error.is_retryable());
126
127        let serialization_error = RustRabbitError::Serialization("test".to_string());
128        assert!(!serialization_error.is_retryable());
129
130        let config_error = RustRabbitError::Configuration("test".to_string());
131        assert!(!config_error.is_retryable());
132
133        let retry_error = RustRabbitError::Retry("test".to_string());
134        assert!(!retry_error.is_retryable());
135    }
136
137    #[test]
138    fn test_connection_error_detection() {
139        let conn_error = RustRabbitError::Connection("test".to_string());
140        assert!(conn_error.is_connection_error());
141
142        let serialization_error = RustRabbitError::Serialization("test".to_string());
143        assert!(!serialization_error.is_connection_error());
144    }
145
146    #[test]
147    fn test_user_messages() {
148        let errors = vec![
149            RustRabbitError::Connection("test".to_string()),
150            RustRabbitError::Serialization("test".to_string()),
151            RustRabbitError::Configuration("test".to_string()),
152        ];
153
154        for error in errors {
155            let message = error.user_message();
156            assert!(!message.is_empty());
157            assert!(!message.contains("test")); // User message should not contain internal details
158        }
159    }
160
161    #[test]
162    fn test_from_serde_json_error() {
163        let json_error = serde_json::from_str::<i32>("invalid json").unwrap_err();
164        let rabbit_error = RustRabbitError::from(json_error);
165
166        matches!(rabbit_error, RustRabbitError::Serialization(_));
167    }
168}