airsprotocols_mcp/transport/
error.rs

1//! Transport Error Types
2//!
3//! This module defines common error types used across all transport implementations.
4
5/// Common transport error types.
6///
7/// This enum provides a standardized set of error variants that can be used
8/// by transport implementations, while still allowing for transport-specific
9/// error types through the `Other` variant.
10#[derive(Debug, thiserror::Error)]
11pub enum TransportError {
12    /// I/O operation failed
13    #[error("I/O error: {0}")]
14    Io(#[from] std::io::Error),
15
16    /// Transport connection is closed
17    #[error("Transport connection is closed")]
18    Closed,
19
20    /// Connection limit exceeded
21    #[error("Connection limit exceeded: {0}")]
22    ConnectionLimit(String),
23
24    /// Invalid connection reference
25    #[error("Invalid connection: {0}")]
26    InvalidConnection(String),
27
28    /// Session management error
29    #[error("Session error: {0}")]
30    SessionError(String),
31
32    /// Message formatting or framing error
33    #[error("Message format error: {message}")]
34    Format { message: String },
35
36    /// Connection timeout
37    #[error("Connection timeout after {duration_ms}ms")]
38    Timeout { duration_ms: u64 },
39
40    /// Buffer overflow or resource exhaustion
41    #[error("Buffer overflow: {details}")]
42    BufferOverflow { details: String },
43
44    /// Message size exceeds maximum allowed
45    #[error("Message too large: {size} bytes (max: {max_size} bytes)")]
46    MessageTooLarge { size: usize, max_size: usize },
47
48    /// Incomplete message received
49    #[error("Incomplete message received")]
50    IncompleteMessage,
51
52    /// JSON parsing error
53    #[error("Parse error: {0}")]
54    ParseError(String),
55
56    /// JSON serialization error
57    #[error("Serialization error: {0}")]
58    SerializationError(String),
59
60    /// Transport-specific error
61    #[error("Transport error: {details}")]
62    Other { details: String },
63}
64
65impl TransportError {
66    /// Create a format error with a message
67    pub fn format(message: impl Into<String>) -> Self {
68        Self::Format {
69            message: message.into(),
70        }
71    }
72
73    /// Create a timeout error with duration
74    pub fn timeout(duration_ms: u64) -> Self {
75        Self::Timeout { duration_ms }
76    }
77
78    /// Create a buffer overflow error with details
79    pub fn buffer_overflow(details: impl Into<String>) -> Self {
80        Self::BufferOverflow {
81            details: details.into(),
82        }
83    }
84
85    /// Create a message too large error
86    pub fn message_too_large(size: usize, max_size: usize) -> Self {
87        Self::MessageTooLarge { size, max_size }
88    }
89
90    /// Create an incomplete message error
91    pub fn incomplete_message() -> Self {
92        Self::IncompleteMessage
93    }
94
95    /// Create a parse error
96    pub fn parse_error(error: impl Into<String>) -> Self {
97        Self::ParseError(error.into())
98    }
99
100    /// Create a serialization error
101    pub fn serialization_error(error: impl Into<String>) -> Self {
102        Self::SerializationError(error.into())
103    }
104
105    /// Create a transport-specific error with details
106    pub fn other(details: impl Into<String>) -> Self {
107        Self::Other {
108            details: details.into(),
109        }
110    }
111
112    /// Create a connection closed error
113    pub fn closed() -> Self {
114        Self::Closed
115    }
116
117    /// Create a connection limit error
118    pub fn connection_limit(message: impl Into<String>) -> Self {
119        Self::ConnectionLimit(message.into())
120    }
121
122    /// Create an invalid connection error
123    pub fn invalid_connection(message: impl Into<String>) -> Self {
124        Self::InvalidConnection(message.into())
125    }
126
127    /// Create a session error
128    pub fn session_error(message: impl Into<String>) -> Self {
129        Self::SessionError(message.into())
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn test_transport_error_creation() {
139        // Test format error
140        let err = TransportError::format("invalid JSON");
141        assert!(matches!(err, TransportError::Format { .. }));
142        assert_eq!(err.to_string(), "Message format error: invalid JSON");
143
144        // Test timeout error
145        let err = TransportError::timeout(5000);
146        assert!(matches!(err, TransportError::Timeout { duration_ms: 5000 }));
147        assert_eq!(err.to_string(), "Connection timeout after 5000ms");
148
149        // Test buffer overflow error
150        let err = TransportError::buffer_overflow("message too large");
151        assert!(matches!(err, TransportError::BufferOverflow { .. }));
152        assert_eq!(err.to_string(), "Buffer overflow: message too large");
153
154        // Test other error
155        let err = TransportError::other("custom transport error");
156        assert!(matches!(err, TransportError::Other { .. }));
157        assert_eq!(err.to_string(), "Transport error: custom transport error");
158
159        // Test closed error
160        let err = TransportError::Closed;
161        assert_eq!(err.to_string(), "Transport connection is closed");
162    }
163
164    #[test]
165    fn test_transport_error_from_io() {
166        let io_err = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "pipe broken");
167        let transport_err = TransportError::from(io_err);
168
169        assert!(matches!(transport_err, TransportError::Io(_)));
170        assert!(transport_err.to_string().contains("pipe broken"));
171    }
172
173    #[test]
174    fn test_transport_error_traits() {
175        let err = TransportError::Closed;
176
177        // Test Error trait
178        assert!(std::error::Error::source(&err).is_none());
179
180        // Test Send + Sync
181        fn assert_send_sync<T: Send + Sync>() {}
182        assert_send_sync::<TransportError>();
183
184        // Test Debug
185        let debug_str = format!("{err:?}");
186        assert!(!debug_str.is_empty());
187    }
188}