Skip to main content

lightstreamer_rs/utils/
error.rs

1/// Unified error type for the Lightstreamer client library.
2///
3/// This enum consolidates all error types that can occur during Lightstreamer
4/// operations, providing typed error handling throughout the library.
5#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
6pub enum LightstreamerError {
7    /// An illegal or inappropriate argument was passed to a method.
8    #[error("invalid argument: {0}")]
9    InvalidArgument(String),
10
11    /// A method was invoked at an illegal or inappropriate time,
12    /// or the object is in an inappropriate state.
13    #[error("invalid state: {0}")]
14    InvalidState(String),
15
16    /// A connection-related error occurred.
17    #[error("connection error: {0}")]
18    Connection(String),
19
20    /// A subscription-related error occurred.
21    #[error("subscription error: {0}")]
22    Subscription(String),
23
24    /// A configuration-related error occurred.
25    #[error("configuration error: {0}")]
26    Configuration(String),
27
28    /// A protocol-related error occurred during message parsing or handling.
29    #[error("protocol error: {0}")]
30    Protocol(String),
31
32    /// A channel communication error occurred.
33    #[error("channel error: {0}")]
34    Channel(String),
35
36    /// A parsing error occurred.
37    #[error("parse error: {0}")]
38    Parse(String),
39
40    /// An authentication error occurred.
41    #[error("authentication error: {0}")]
42    Authentication(String),
43
44    /// A network error occurred.
45    #[error("network error: {0}")]
46    Network(String),
47
48    /// A timeout occurred.
49    #[error("timeout: {0}")]
50    Timeout(String),
51
52    /// A lock acquisition error occurred.
53    #[error("lock error: {0}")]
54    Lock(String),
55}
56
57impl LightstreamerError {
58    /// Creates an invalid argument error.
59    #[cold]
60    #[must_use]
61    #[inline]
62    pub fn invalid_argument(msg: impl Into<String>) -> Self {
63        Self::InvalidArgument(msg.into())
64    }
65
66    /// Creates an invalid state error.
67    #[cold]
68    #[must_use]
69    #[inline]
70    pub fn invalid_state(msg: impl Into<String>) -> Self {
71        Self::InvalidState(msg.into())
72    }
73
74    /// Creates a connection error.
75    #[cold]
76    #[must_use]
77    #[inline]
78    pub fn connection(msg: impl Into<String>) -> Self {
79        Self::Connection(msg.into())
80    }
81
82    /// Creates a subscription error.
83    #[cold]
84    #[must_use]
85    #[inline]
86    pub fn subscription(msg: impl Into<String>) -> Self {
87        Self::Subscription(msg.into())
88    }
89
90    /// Creates a configuration error.
91    #[cold]
92    #[must_use]
93    #[inline]
94    pub fn configuration(msg: impl Into<String>) -> Self {
95        Self::Configuration(msg.into())
96    }
97
98    /// Creates a protocol error.
99    #[cold]
100    #[must_use]
101    #[inline]
102    pub fn protocol(msg: impl Into<String>) -> Self {
103        Self::Protocol(msg.into())
104    }
105
106    /// Creates a channel error.
107    #[cold]
108    #[must_use]
109    #[inline]
110    pub fn channel(msg: impl Into<String>) -> Self {
111        Self::Channel(msg.into())
112    }
113
114    /// Creates a parse error.
115    #[cold]
116    #[must_use]
117    #[inline]
118    pub fn parse(msg: impl Into<String>) -> Self {
119        Self::Parse(msg.into())
120    }
121
122    /// Creates a lock error.
123    #[cold]
124    #[must_use]
125    #[inline]
126    pub fn lock(msg: impl Into<String>) -> Self {
127        Self::Lock(msg.into())
128    }
129}
130
131/// Type alias for Results using LightstreamerError.
132pub type Result<T> = std::result::Result<T, LightstreamerError>;
133
134impl From<String> for LightstreamerError {
135    fn from(s: String) -> Self {
136        LightstreamerError::InvalidArgument(s)
137    }
138}
139
140impl From<&str> for LightstreamerError {
141    fn from(s: &str) -> Self {
142        LightstreamerError::InvalidArgument(s.to_string())
143    }
144}
145
146impl From<Box<dyn std::error::Error>> for LightstreamerError {
147    fn from(e: Box<dyn std::error::Error>) -> Self {
148        LightstreamerError::InvalidState(e.to_string())
149    }
150}
151
152impl From<Box<dyn std::error::Error + Send + Sync>> for LightstreamerError {
153    fn from(e: Box<dyn std::error::Error + Send + Sync>) -> Self {
154        LightstreamerError::InvalidState(e.to_string())
155    }
156}
157
158impl From<tokio_tungstenite::tungstenite::http::Error> for LightstreamerError {
159    fn from(e: tokio_tungstenite::tungstenite::http::Error) -> Self {
160        LightstreamerError::InvalidState(e.to_string())
161    }
162}
163
164impl From<serde_urlencoded::ser::Error> for LightstreamerError {
165    fn from(e: serde_urlencoded::ser::Error) -> Self {
166        LightstreamerError::InvalidArgument(e.to_string())
167    }
168}
169
170impl From<tokio_tungstenite::tungstenite::Error> for LightstreamerError {
171    fn from(e: tokio_tungstenite::tungstenite::Error) -> Self {
172        LightstreamerError::Connection(e.to_string())
173    }
174}
175
176impl From<std::io::Error> for LightstreamerError {
177    fn from(e: std::io::Error) -> Self {
178        LightstreamerError::Connection(e.to_string())
179    }
180}
181
182impl From<tokio::sync::mpsc::error::SendError<crate::client::SubscriptionRequest>>
183    for LightstreamerError
184{
185    fn from(e: tokio::sync::mpsc::error::SendError<crate::client::SubscriptionRequest>) -> Self {
186        LightstreamerError::InvalidState(e.to_string())
187    }
188}
189
190impl From<tokio::sync::mpsc::error::TrySendError<usize>> for LightstreamerError {
191    fn from(e: tokio::sync::mpsc::error::TrySendError<usize>) -> Self {
192        LightstreamerError::Channel(e.to_string())
193    }
194}
195
196impl From<crate::connection::ReconnectionError> for LightstreamerError {
197    fn from(e: crate::connection::ReconnectionError) -> Self {
198        LightstreamerError::Connection(e.to_string())
199    }
200}
201
202impl From<std::env::VarError> for LightstreamerError {
203    fn from(e: std::env::VarError) -> Self {
204        LightstreamerError::Configuration(e.to_string())
205    }
206}
207
208/// Legacy exception type for backward compatibility.
209/// Prefer using `LightstreamerError::InvalidArgument` directly.
210#[deprecated(
211    since = "0.3.0",
212    note = "Use LightstreamerError::InvalidArgument instead"
213)]
214pub type IllegalArgumentException = LightstreamerError;
215
216/// Legacy exception type for backward compatibility.
217/// Prefer using `LightstreamerError::InvalidState` directly.
218#[deprecated(since = "0.3.0", note = "Use LightstreamerError::InvalidState instead")]
219pub type IllegalStateException = LightstreamerError;
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224    use std::error::Error;
225
226    #[test]
227    fn test_invalid_argument_error() -> Result<()> {
228        let error = LightstreamerError::invalid_argument("Test error message");
229
230        // Test Debug implementation
231        let debug_output = format!("{:?}", error);
232        assert!(debug_output.contains("InvalidArgument"));
233        assert!(debug_output.contains("Test error message"));
234
235        // Test Display implementation
236        assert_eq!(error.to_string(), "invalid argument: Test error message");
237
238        Ok(())
239    }
240
241    #[test]
242    fn test_invalid_state_error() -> Result<()> {
243        let error = LightstreamerError::invalid_state("Test state error");
244
245        // Test Debug implementation
246        let debug_output = format!("{:?}", error);
247        assert!(debug_output.contains("InvalidState"));
248        assert!(debug_output.contains("Test state error"));
249
250        // Test Display implementation
251        assert_eq!(error.to_string(), "invalid state: Test state error");
252
253        Ok(())
254    }
255
256    #[test]
257    fn test_all_error_variants() -> Result<()> {
258        let errors = vec![
259            (
260                LightstreamerError::connection("conn"),
261                "connection error: conn",
262            ),
263            (
264                LightstreamerError::subscription("sub"),
265                "subscription error: sub",
266            ),
267            (
268                LightstreamerError::configuration("cfg"),
269                "configuration error: cfg",
270            ),
271            (
272                LightstreamerError::protocol("proto"),
273                "protocol error: proto",
274            ),
275            (LightstreamerError::channel("chan"), "channel error: chan"),
276            (LightstreamerError::parse("parse"), "parse error: parse"),
277            (LightstreamerError::lock("lock"), "lock error: lock"),
278        ];
279
280        for (error, expected_msg) in errors {
281            assert_eq!(error.to_string(), expected_msg);
282        }
283
284        Ok(())
285    }
286
287    #[test]
288    fn test_error_propagation() -> Result<()> {
289        fn function_that_fails() -> Result<()> {
290            Err(LightstreamerError::invalid_argument("Test propagation"))
291        }
292
293        fn propagate_error() -> Result<()> {
294            function_that_fails()?;
295            Ok(())
296        }
297
298        let result = propagate_error();
299        assert!(result.is_err());
300        if let Err(error) = result {
301            assert_eq!(error.to_string(), "invalid argument: Test propagation");
302        }
303
304        Ok(())
305    }
306
307    #[test]
308    fn test_error_conversion_to_box_dyn() -> Result<()> {
309        let error = LightstreamerError::invalid_argument("Test conversion");
310        let boxed_error: Box<dyn Error> = Box::new(error);
311        assert_eq!(boxed_error.to_string(), "invalid argument: Test conversion");
312
313        Ok(())
314    }
315
316    #[test]
317    fn test_error_as_return_type() -> Result<()> {
318        fn may_fail(value: i32) -> Result<()> {
319            if value < 0 {
320                Err(LightstreamerError::invalid_argument(
321                    "Value cannot be negative",
322                ))
323            } else if value > 100 {
324                Err(LightstreamerError::invalid_state("Value too large"))
325            } else {
326                Ok(())
327            }
328        }
329
330        let result = may_fail(-10);
331        assert!(result.is_err());
332        if let Err(error) = result {
333            assert_eq!(
334                error.to_string(),
335                "invalid argument: Value cannot be negative"
336            );
337        }
338
339        let result = may_fail(150);
340        assert!(result.is_err());
341        if let Err(error) = result {
342            assert_eq!(error.to_string(), "invalid state: Value too large");
343        }
344
345        let result = may_fail(50);
346        assert!(result.is_ok());
347
348        Ok(())
349    }
350
351    #[test]
352    fn test_error_trait_methods() -> Result<()> {
353        let error = LightstreamerError::invalid_argument("Test error");
354        let error_ref: &dyn Error = &error;
355
356        // Test source method (should be None since we don't have a cause)
357        assert!(error_ref.source().is_none());
358
359        Ok(())
360    }
361
362    #[test]
363    fn test_error_equality() -> Result<()> {
364        let error1 = LightstreamerError::invalid_argument("test");
365        let error2 = LightstreamerError::invalid_argument("test");
366        let error3 = LightstreamerError::invalid_state("test");
367
368        assert_eq!(error1, error2);
369        assert_ne!(error1, error3);
370
371        Ok(())
372    }
373
374    #[test]
375    fn test_error_clone() -> Result<()> {
376        let error = LightstreamerError::connection("test connection");
377        let cloned = error.clone();
378        assert_eq!(error, cloned);
379
380        Ok(())
381    }
382}