Skip to main content

ddp_rs/
error.rs

1//! Error types for DDP operations.
2//!
3//! This module defines all error types that can occur when working with DDP connections.
4//!
5//! [`DDPError`] is only available with the `std` feature: every variant wraps a std type
6//! (sockets, `serde_json`, the crossbeam channel). `no_std` builds construct frames and parse
7//! packets without it — [`crate::protocol::FrameBuilder`] and [`crate::packet::PacketRef`] are
8//! infallible / use `Option`.
9
10#[cfg(feature = "std")]
11use thiserror::Error;
12
13/// Errors that can occur during DDP operations.
14///
15/// All errors implement the standard [`std::error::Error`] trait via `thiserror`.
16#[cfg(feature = "std")]
17#[derive(Error, Debug)]
18pub enum DDPError {
19    /// Socket or network I/O error
20    #[error("socket error")]
21    Disconnect(#[from] std::io::Error),
22
23    /// Failed to resolve the provided address
24    #[error("No valid socket addr found")]
25    NoValidSocketAddr,
26
27    /// JSON parsing error for control messages
28    #[error("parse error")]
29    ParseError(#[from] serde_json::Error),
30
31    /// Received data from an unknown or unexpected client
32    #[error("invalid sender, did you forget to connect() ( data from {from:?} - {data:?})")]
33    UnknownClient {
34        /// The address that sent the unexpected data
35        from: std::net::SocketAddr,
36        /// The unexpected data received
37        data: Vec<u8>,
38    },
39
40    /// Received packet with invalid format or structure
41    #[error("Invalid packet")]
42    InvalidPacket,
43
44    /// No packets are currently available to receive (non-blocking operation)
45    #[error("There are no packets waiting to be read. This error should be handled explicitly")]
46    NothingToReceive,
47
48    /// Error from the internal packet receiver channel
49    #[error("Error receiving packet: {0}")]
50    CrossBeamError(#[from] crossbeam::channel::TryRecvError),
51}
52
53#[cfg(all(test, feature = "std"))]
54mod tests {
55    use super::*;
56    use std::net::{IpAddr, Ipv4Addr, SocketAddr};
57
58    #[test]
59    fn test_error_display_disconnect() {
60        let io_error = std::io::Error::new(std::io::ErrorKind::ConnectionReset, "connection reset");
61        let error = DDPError::Disconnect(io_error);
62        assert_eq!(error.to_string(), "socket error");
63    }
64
65    #[test]
66    fn test_error_display_no_valid_socket_addr() {
67        let error = DDPError::NoValidSocketAddr;
68        assert_eq!(error.to_string(), "No valid socket addr found");
69    }
70
71    #[test]
72    fn test_error_display_parse_error() {
73        let json_error = serde_json::from_str::<serde_json::Value>("{invalid json")
74            .expect_err("should fail to parse");
75        let error = DDPError::ParseError(json_error);
76        assert_eq!(error.to_string(), "parse error");
77    }
78
79    #[test]
80    fn test_error_display_unknown_client() {
81        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080);
82        let data = vec![0x01, 0x02, 0x03];
83        let error = DDPError::UnknownClient {
84            from: addr,
85            data: data.clone(),
86        };
87
88        let error_str = error.to_string();
89        assert!(error_str.contains("invalid sender"));
90        assert!(error_str.contains("192.168.1.1:8080"));
91        assert!(error_str.contains("[1, 2, 3]"));
92    }
93
94    #[test]
95    fn test_error_display_invalid_packet() {
96        let error = DDPError::InvalidPacket;
97        assert_eq!(error.to_string(), "Invalid packet");
98    }
99
100    #[test]
101    fn test_error_display_nothing_to_receive() {
102        let error = DDPError::NothingToReceive;
103        assert_eq!(
104            error.to_string(),
105            "There are no packets waiting to be read. This error should be handled explicitly"
106        );
107    }
108
109    #[test]
110    fn test_error_display_crossbeam_error() {
111        use crossbeam::channel::TryRecvError;
112
113        let error = DDPError::CrossBeamError(TryRecvError::Empty);
114        assert!(error.to_string().contains("Error receiving packet"));
115    }
116
117    #[test]
118    fn test_error_from_io_error() {
119        let io_error = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "broken pipe");
120        let error: DDPError = io_error.into();
121
122        match error {
123            DDPError::Disconnect(_) => {},
124            _ => panic!("Expected Disconnect variant"),
125        }
126    }
127
128    #[test]
129    fn test_error_from_json_error() {
130        let json_error = serde_json::from_str::<serde_json::Value>("{bad}")
131            .expect_err("should fail");
132        let error: DDPError = json_error.into();
133
134        match error {
135            DDPError::ParseError(_) => {},
136            _ => panic!("Expected ParseError variant"),
137        }
138    }
139
140    #[test]
141    fn test_error_from_crossbeam_error() {
142        use crossbeam::channel::TryRecvError;
143
144        let crossbeam_error = TryRecvError::Disconnected;
145        let error: DDPError = crossbeam_error.into();
146
147        match error {
148            DDPError::CrossBeamError(_) => {},
149            _ => panic!("Expected CrossBeamError variant"),
150        }
151    }
152
153    #[test]
154    fn test_error_debug_format() {
155        let error = DDPError::InvalidPacket;
156        let debug_str = format!("{:?}", error);
157        assert_eq!(debug_str, "InvalidPacket");
158    }
159
160    #[test]
161    fn test_unknown_client_error_fields() {
162        let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)), 4048);
163        let data = vec![0xDE, 0xAD, 0xBE, 0xEF];
164
165        let error = DDPError::UnknownClient {
166            from: addr,
167            data: data.clone(),
168        };
169
170        match error {
171            DDPError::UnknownClient { from, data: d } => {
172                assert_eq!(from, addr);
173                assert_eq!(d, data);
174            }
175            _ => panic!("Expected UnknownClient variant"),
176        }
177    }
178
179    #[test]
180    fn test_error_is_send_sync() {
181        fn assert_send_sync<T: Send + Sync>() {}
182        assert_send_sync::<DDPError>();
183    }
184}