mcp_daemon/transport/
error.rs

1use std::fmt;
2use thiserror::Error;
3use std::error::Error as StdError;
4
5/// Transport-specific error codes
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum TransportErrorCode {
8    // Connection errors
9    /// Connection to transport failed
10    ConnectionFailed = -1000,
11    /// Connection was closed
12    ConnectionClosed = -1001,
13    /// Connection timed out
14    ConnectionTimeout = -1002,
15
16    // Message errors
17    /// Message size exceeds limit
18    MessageTooLarge = -1100,
19    /// Message format is invalid
20    InvalidMessage = -1101,
21    /// Failed to send message
22    MessageSendFailed = -1102,
23    /// Failed to receive message
24    MessageReceiveFailed = -1103,
25
26    // Protocol errors
27    /// Protocol error occurred
28    ProtocolError = -1200,
29    /// Transport handshake failed
30    HandshakeFailed = -1201,
31
32    // Transport operation errors
33    /// Error sending message
34    SendError = -1300,
35    /// Error opening transport
36    OpenError = -1301,
37    /// Error closing transport
38    CloseError = -1302,
39    /// Error receiving message
40    ReceiveError = -1303,
41    /// Authentication failed
42    AuthenticationFailed = -1202,
43
44    // Session errors
45    /// Session has expired
46    SessionExpired = -1310,
47    /// Session is invalid
48    SessionInvalid = -1311,
49    /// Session not found
50    SessionNotFound = -1312,
51
52    // WebSocket specific
53    /// WebSocket upgrade failed
54    WebSocketUpgradeFailed = -1400,
55    /// WebSocket protocol error
56    WebSocketProtocolError = -1401,
57    /// WebSocket frame error
58    WebSocketFrameError = -1402,
59
60    // SSE specific
61    /// SSE connection failed to establish
62    SseConnectionFailed = -1500,
63    /// Error occurred while streaming SSE events
64    SseStreamError = -1501,
65    /// Failed to parse SSE event data
66    SseParseError = -1502,
67
68    // Generic errors
69    /// Internal transport error
70    InternalError = -1900,
71    /// Transport operation timed out
72    Timeout = -1901,
73    /// Transport is in an invalid state
74    InvalidState = -1902,
75}
76
77impl fmt::Display for TransportErrorCode {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            // Connection errors
81            Self::ConnectionFailed => write!(f, "Failed to establish connection"),
82            Self::ConnectionClosed => write!(f, "Connection was closed"),
83            Self::ConnectionTimeout => write!(f, "Connection timed out"),
84
85            // Message errors
86            Self::MessageTooLarge => write!(f, "Message exceeds size limit"),
87            Self::InvalidMessage => write!(f, "Invalid message format"),
88            Self::MessageSendFailed => write!(f, "Failed to send message"),
89            Self::MessageReceiveFailed => write!(f, "Failed to receive message"),
90
91            // Protocol errors
92            Self::ProtocolError => write!(f, "Protocol error"),
93            Self::HandshakeFailed => write!(f, "Handshake failed"),
94            Self::AuthenticationFailed => write!(f, "Authentication failed"),
95
96            // Session errors
97            Self::SessionExpired => write!(f, "Session has expired"),
98            Self::SessionInvalid => write!(f, "Invalid session"),
99            Self::SessionNotFound => write!(f, "Session not found"),
100
101            // WebSocket specific
102            Self::WebSocketUpgradeFailed => write!(f, "WebSocket upgrade failed"),
103            Self::WebSocketProtocolError => write!(f, "WebSocket protocol error"),
104            Self::WebSocketFrameError => write!(f, "WebSocket frame error"),
105
106            // SSE specific
107            Self::SseConnectionFailed => write!(f, "SSE connection failed"),
108            Self::SseStreamError => write!(f, "SSE stream error"),
109            Self::SseParseError => write!(f, "SSE parse error"),
110
111            // Generic errors
112            Self::InternalError => write!(f, "Internal error"),
113            Self::Timeout => write!(f, "Operation timed out"),
114            Self::InvalidState => write!(f, "Invalid state"),
115            Self::SendError => write!(f, "Send error"),
116            Self::OpenError => write!(f, "Open error"),
117            Self::CloseError => write!(f, "Close error"),
118            Self::ReceiveError => write!(f, "Receive error"),
119        }
120    }
121}
122
123/// Transport-specific error type
124#[derive(Error, Debug)]
125pub enum TransportError {
126    #[error("{code}: {message}")]
127    /// Transport-specific error
128    Transport {
129        /// The error code
130        code: TransportErrorCode,
131        /// Error message
132        message: String,
133        #[source]
134        /// Optional error source
135        source: Option<Box<dyn StdError + Send + Sync>>,
136    },
137
138    #[error("JSON error: {0}")]
139    /// JSON serialization/deserialization error
140    Json(#[from] serde_json::Error),
141
142    #[error("I/O error: {0}")]
143    /// I/O error
144    Io(#[from] std::io::Error),
145
146    #[error("WebSocket error: {0}")]
147    /// WebSocket error
148    WebSocket(#[from] tokio_tungstenite::tungstenite::Error),
149
150    #[error("HTTP error: {0}")]
151    /// HTTP error
152    Http(#[from] reqwest::Error),
153
154    #[error("Channel error: {0}")]
155    /// Channel communication error
156    Channel(String),
157
158    #[error("UTF-8 error: {0}")]
159    /// UTF-8 encoding/decoding error
160    Utf8(#[from] std::string::FromUtf8Error),
161
162    #[error("System time error: {0}")]
163    /// System time error
164    SystemTime(#[from] std::time::SystemTimeError),
165
166    #[error("JWT error: {0}")]
167    /// JWT token error
168    Jwt(#[from] jsonwebtoken::errors::Error),
169}
170
171impl<T> From<tokio::sync::mpsc::error::SendError<T>> for TransportError {
172    fn from(err: tokio::sync::mpsc::error::SendError<T>) -> Self {
173        Self::Channel(err.to_string())
174    }
175}
176
177impl<T> From<tokio::sync::broadcast::error::SendError<T>> for TransportError {
178    fn from(err: tokio::sync::broadcast::error::SendError<T>) -> Self {
179        Self::Channel(err.to_string())
180    }
181}
182
183impl From<actix_web::Error> for TransportError {
184    fn from(err: actix_web::Error) -> Self {
185        Self::Transport {
186            code: TransportErrorCode::InternalError,
187            message: err.to_string(),
188            source: None, // Don't store the source since actix_web::Error doesn't implement Send + Sync
189        }
190    }
191}
192
193impl From<tokio::task::JoinError> for TransportError {
194    fn from(err: tokio::task::JoinError) -> Self {
195        Self::Transport {
196            code: TransportErrorCode::InternalError,
197            message: err.to_string(),
198            source: None,
199        }
200    }
201}
202
203impl TransportError {
204    /// Create a new transport error
205    pub fn new(code: TransportErrorCode, message: impl Into<String>) -> Self {
206        Self::Transport {
207            code,
208            message: message.into(),
209            source: None,
210        }
211    }
212
213    /// Create a new transport error with source
214    pub fn with_source(
215        code: TransportErrorCode,
216        message: impl Into<String>,
217        source: impl Into<Box<dyn StdError + Send + Sync>>,
218    ) -> Self {
219        Self::Transport {
220            code,
221            message: message.into(),
222            source: Some(source.into()),
223        }
224    }
225
226    /// Get the error code if this is a transport error
227    pub fn code(&self) -> Option<TransportErrorCode> {
228        match self {
229            Self::Transport { code, .. } => Some(*code),
230            _ => None,
231        }
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_error_codes() {
241        assert_eq!(TransportErrorCode::ConnectionFailed as i32, -1000);
242        assert_eq!(TransportErrorCode::MessageTooLarge as i32, -1100);
243        assert_eq!(TransportErrorCode::ProtocolError as i32, -1200);
244        assert_eq!(TransportErrorCode::SendError as i32, -1300);
245        assert_eq!(TransportErrorCode::SessionExpired as i32, -1310);
246    }
247
248    #[test]
249    fn test_error_display() {
250        let error = TransportError::new(TransportErrorCode::ConnectionFailed, "Failed to connect");
251        assert_eq!(error.to_string(), "Failed to establish connection: Failed to connect");
252
253        let io_error = std::io::Error::new(std::io::ErrorKind::Other, "IO error");
254        let error = TransportError::with_source(
255            TransportErrorCode::ConnectionFailed,
256            "Failed to connect",
257            Box::new(io_error) as Box<dyn StdError + Send + Sync>,
258        );
259        assert_eq!(error.to_string(), "Failed to establish connection: Failed to connect");
260    }
261
262    #[test]
263    fn test_error_code() {
264        let error = TransportError::new(TransportErrorCode::ConnectionFailed, "Failed to connect");
265        assert_eq!(error.code(), Some(TransportErrorCode::ConnectionFailed));
266
267        let io_error = std::io::Error::new(std::io::ErrorKind::Other, "JSON error");
268        let error = TransportError::Json(serde_json::Error::io(io_error));
269        assert_eq!(error.code(), None);
270    }
271}