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