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