ant_quic/relay/
error.rs

1//! Error types for the relay protocol implementation.
2
3use std::fmt;
4
5/// Result type alias for relay operations
6pub type RelayResult<T> = Result<T, RelayError>;
7
8/// Comprehensive error taxonomy for relay operations
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum RelayError {
11    /// Authentication failed due to invalid token or signature
12    AuthenticationFailed { reason: String },
13
14    /// Rate limiting triggered - too many requests
15    RateLimitExceeded { retry_after_ms: u64 },
16
17    /// Session-related errors
18    SessionError {
19        session_id: Option<u32>,
20        kind: SessionErrorKind,
21    },
22
23    /// Network connectivity issues
24    NetworkError { operation: String, source: String },
25
26    /// Protocol-level errors
27    ProtocolError { frame_type: u8, reason: String },
28
29    /// Resource exhaustion (memory, bandwidth, etc.)
30    ResourceExhausted {
31        resource_type: String,
32        current_usage: u64,
33        limit: u64,
34    },
35
36    /// Configuration or setup errors
37    ConfigurationError { parameter: String, reason: String },
38}
39
40/// Specific session error types
41#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum SessionErrorKind {
43    /// Session not found
44    NotFound,
45    /// Session already exists
46    AlreadyExists,
47    /// Session expired
48    Expired,
49    /// Session terminated
50    Terminated,
51    /// Invalid session state for operation
52    InvalidState {
53        current_state: String,
54        expected_state: String,
55    },
56    /// Bandwidth limit exceeded for session
57    BandwidthExceeded { used: u64, limit: u64 },
58}
59
60impl fmt::Display for RelayError {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        match self {
63            RelayError::AuthenticationFailed { reason } => {
64                write!(f, "Authentication failed: {}", reason)
65            }
66            RelayError::RateLimitExceeded { retry_after_ms } => {
67                write!(f, "Rate limit exceeded, retry after {} ms", retry_after_ms)
68            }
69            RelayError::SessionError { session_id, kind } => match session_id {
70                Some(id) => write!(f, "Session {} error: {}", id, kind),
71                None => write!(f, "Session error: {}", kind),
72            },
73            RelayError::NetworkError { operation, source } => {
74                write!(f, "Network error during {}: {}", operation, source)
75            }
76            RelayError::ProtocolError { frame_type, reason } => {
77                write!(
78                    f,
79                    "Protocol error in frame 0x{:02x}: {}",
80                    frame_type, reason
81                )
82            }
83            RelayError::ResourceExhausted {
84                resource_type,
85                current_usage,
86                limit,
87            } => {
88                write!(
89                    f,
90                    "Resource exhausted: {} usage ({}) exceeds limit ({})",
91                    resource_type, current_usage, limit
92                )
93            }
94            RelayError::ConfigurationError { parameter, reason } => {
95                write!(f, "Configuration error for {}: {}", parameter, reason)
96            }
97        }
98    }
99}
100
101impl fmt::Display for SessionErrorKind {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        match self {
104            SessionErrorKind::NotFound => write!(f, "session not found"),
105            SessionErrorKind::AlreadyExists => write!(f, "session already exists"),
106            SessionErrorKind::Expired => write!(f, "session expired"),
107            SessionErrorKind::Terminated => write!(f, "session terminated"),
108            SessionErrorKind::InvalidState {
109                current_state,
110                expected_state,
111            } => {
112                write!(
113                    f,
114                    "invalid state '{}', expected '{}'",
115                    current_state, expected_state
116                )
117            }
118            SessionErrorKind::BandwidthExceeded { used, limit } => {
119                write!(f, "bandwidth exceeded: {} > {}", used, limit)
120            }
121        }
122    }
123}
124
125impl std::error::Error for RelayError {}
126
127impl From<std::io::Error> for RelayError {
128    fn from(error: std::io::Error) -> Self {
129        RelayError::NetworkError {
130            operation: "I/O operation".to_string(),
131            source: error.to_string(),
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn test_error_display() {
142        let auth_error = RelayError::AuthenticationFailed {
143            reason: "Invalid signature".to_string(),
144        };
145        assert!(auth_error.to_string().contains("Authentication failed"));
146
147        let rate_limit_error = RelayError::RateLimitExceeded {
148            retry_after_ms: 1000,
149        };
150        assert!(rate_limit_error.to_string().contains("Rate limit exceeded"));
151
152        let session_error = RelayError::SessionError {
153            session_id: Some(123),
154            kind: SessionErrorKind::NotFound,
155        };
156        assert!(session_error.to_string().contains("Session 123 error"));
157    }
158
159    #[test]
160    fn test_session_error_kind_display() {
161        let invalid_state = SessionErrorKind::InvalidState {
162            current_state: "Connected".to_string(),
163            expected_state: "Idle".to_string(),
164        };
165        assert!(invalid_state.to_string().contains("invalid state"));
166        assert!(invalid_state.to_string().contains("Connected"));
167        assert!(invalid_state.to_string().contains("Idle"));
168    }
169
170    #[test]
171    fn test_error_conversion() {
172        let io_error =
173            std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "Connection refused");
174        let relay_error: RelayError = io_error.into();
175
176        match relay_error {
177            RelayError::NetworkError { operation, source } => {
178                assert_eq!(operation, "I/O operation");
179                assert!(source.contains("Connection refused"));
180            }
181            _ => panic!("Expected NetworkError"),
182        }
183    }
184}