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
46                matches!(
47                    lapin_error,
48                    lapin::Error::IOError(_) | lapin::Error::ProtocolError(_)
49                )
50            }
51            // Serialization and configuration errors are not retryable
52            RustRabbitError::Serialization(_) => false,
53            RustRabbitError::Configuration(_) => false,
54            // Consumer/Publisher errors might be retryable depending on context
55            RustRabbitError::Consumer(_) => true,
56            RustRabbitError::Publisher(_) => true,
57            // Retry errors are not retryable (to avoid infinite loops)
58            RustRabbitError::Retry(_) => false,
59            // IO errors might be retryable
60            RustRabbitError::Io(_) => true,
61        }
62    }
63
64    /// Check if the error indicates a connection issue
65    pub fn is_connection_error(&self) -> bool {
66        matches!(
67            self,
68            RustRabbitError::Connection(_) | RustRabbitError::Protocol(_) | RustRabbitError::Io(_)
69        )
70    }
71
72    /// Get a user-friendly error message
73    pub fn user_message(&self) -> String {
74        match self {
75            RustRabbitError::Connection(_) => {
76                "Failed to connect to RabbitMQ. Please check your connection settings.".to_string()
77            }
78            RustRabbitError::Protocol(_) => {
79                "RabbitMQ protocol error. The connection might be unstable.".to_string()
80            }
81            RustRabbitError::Serialization(_) => {
82                "Failed to serialize/deserialize message. Please check your message format."
83                    .to_string()
84            }
85            RustRabbitError::Configuration(_) => {
86                "Configuration error. Please check your settings.".to_string()
87            }
88            RustRabbitError::Consumer(_) => {
89                "Consumer error. Failed to process message.".to_string()
90            }
91            RustRabbitError::Publisher(_) => "Publisher error. Failed to send message.".to_string(),
92            RustRabbitError::Retry(_) => {
93                "Retry mechanism failed. Message processing could not be completed.".to_string()
94            }
95            RustRabbitError::Io(_) => {
96                "IO error occurred. This might be a temporary issue.".to_string()
97            }
98        }
99    }
100}
101
102/// Convert serde_json errors to RustRabbitError
103impl From<serde_json::Error> for RustRabbitError {
104    fn from(err: serde_json::Error) -> Self {
105        RustRabbitError::Serialization(err.to_string())
106    }
107}
108
109/// Convert URL parsing errors to RustRabbitError
110impl From<url::ParseError> for RustRabbitError {
111    fn from(err: url::ParseError) -> Self {
112        RustRabbitError::Configuration(format!("Invalid URL: {}", err))
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_error_retryable() {
122        let conn_error = RustRabbitError::Connection("test".to_string());
123        assert!(conn_error.is_retryable());
124
125        let serialization_error = RustRabbitError::Serialization("test".to_string());
126        assert!(!serialization_error.is_retryable());
127
128        let config_error = RustRabbitError::Configuration("test".to_string());
129        assert!(!config_error.is_retryable());
130
131        let retry_error = RustRabbitError::Retry("test".to_string());
132        assert!(!retry_error.is_retryable());
133    }
134
135    #[test]
136    fn test_connection_error_detection() {
137        let conn_error = RustRabbitError::Connection("test".to_string());
138        assert!(conn_error.is_connection_error());
139
140        let serialization_error = RustRabbitError::Serialization("test".to_string());
141        assert!(!serialization_error.is_connection_error());
142    }
143
144    #[test]
145    fn test_user_messages() {
146        let errors = vec![
147            RustRabbitError::Connection("test".to_string()),
148            RustRabbitError::Serialization("test".to_string()),
149            RustRabbitError::Configuration("test".to_string()),
150        ];
151
152        for error in errors {
153            let message = error.user_message();
154            assert!(!message.is_empty());
155            assert!(!message.contains("test")); // User message should not contain internal details
156        }
157    }
158
159    #[test]
160    fn test_from_serde_json_error() {
161        let json_error = serde_json::from_str::<i32>("invalid json").unwrap_err();
162        let rabbit_error = RustRabbitError::from(json_error);
163
164        matches!(rabbit_error, RustRabbitError::Serialization(_));
165    }
166}