ant_quic/relay/
error.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8//! Error types for the relay protocol implementation.
9
10use std::fmt;
11
12/// Result type alias for relay operations
13pub type RelayResult<T> = Result<T, RelayError>;
14
15/// Comprehensive error taxonomy for relay operations
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum RelayError {
18    /// Authentication failed due to invalid token or signature
19    AuthenticationFailed {
20        /// Human-readable reason for authentication failure
21        reason: String,
22    },
23
24    /// Rate limiting triggered - too many requests
25    RateLimitExceeded {
26        /// Suggested wait time before retrying, in milliseconds
27        retry_after_ms: u64,
28    },
29
30    /// Session-related errors
31    SessionError {
32        /// Optional session identifier if known
33        session_id: Option<u32>,
34        /// Specific category of session error
35        kind: SessionErrorKind,
36    },
37
38    /// Network connectivity issues
39    NetworkError {
40        /// The operation being performed when the error occurred
41        operation: String,
42        /// The underlying error source description
43        source: String,
44    },
45
46    /// Protocol-level errors
47    ProtocolError {
48        /// Offending frame type identifier
49        frame_type: u8,
50        /// Human-readable explanation of the violation
51        reason: String,
52    },
53
54    /// Resource exhaustion (memory, bandwidth, etc.)
55    ResourceExhausted {
56        /// Type of resource that was exceeded (e.g. "buffer", "sessions")
57        resource_type: String,
58        /// Current measured usage of the resource
59        current_usage: u64,
60        /// Configured limit for the resource
61        limit: u64,
62    },
63
64    /// Configuration or setup errors
65    ConfigurationError {
66        /// The configuration parameter that is invalid
67        parameter: String,
68        /// Explanation of why the parameter is invalid
69        reason: String,
70    },
71}
72
73/// Specific session error types
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum SessionErrorKind {
76    /// Session not found
77    NotFound,
78    /// Session already exists
79    AlreadyExists,
80    /// Session expired
81    Expired,
82    /// Session terminated
83    Terminated,
84    /// Invalid session state for operation
85    InvalidState {
86        /// The current state encountered
87        current_state: String,
88        /// The state that was expected
89        expected_state: String,
90    },
91    /// Bandwidth limit exceeded for session
92    BandwidthExceeded {
93        /// Bytes used
94        used: u64,
95        /// Configured limit in bytes
96        limit: u64,
97    },
98}
99
100impl fmt::Display for RelayError {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match self {
103            RelayError::AuthenticationFailed { reason } => {
104                write!(f, "Authentication failed: {}", reason)
105            }
106            RelayError::RateLimitExceeded { retry_after_ms } => {
107                write!(f, "Rate limit exceeded, retry after {} ms", retry_after_ms)
108            }
109            RelayError::SessionError { session_id, kind } => match session_id {
110                Some(id) => write!(f, "Session {} error: {}", id, kind),
111                None => write!(f, "Session error: {}", kind),
112            },
113            RelayError::NetworkError { operation, source } => {
114                write!(f, "Network error during {}: {}", operation, source)
115            }
116            RelayError::ProtocolError { frame_type, reason } => {
117                write!(
118                    f,
119                    "Protocol error in frame 0x{:02x}: {}",
120                    frame_type, reason
121                )
122            }
123            RelayError::ResourceExhausted {
124                resource_type,
125                current_usage,
126                limit,
127            } => {
128                write!(
129                    f,
130                    "Resource exhausted: {} usage ({}) exceeds limit ({})",
131                    resource_type, current_usage, limit
132                )
133            }
134            RelayError::ConfigurationError { parameter, reason } => {
135                write!(f, "Configuration error for {}: {}", parameter, reason)
136            }
137        }
138    }
139}
140
141impl fmt::Display for SessionErrorKind {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        match self {
144            SessionErrorKind::NotFound => write!(f, "session not found"),
145            SessionErrorKind::AlreadyExists => write!(f, "session already exists"),
146            SessionErrorKind::Expired => write!(f, "session expired"),
147            SessionErrorKind::Terminated => write!(f, "session terminated"),
148            SessionErrorKind::InvalidState {
149                current_state,
150                expected_state,
151            } => {
152                write!(
153                    f,
154                    "invalid state '{}', expected '{}'",
155                    current_state, expected_state
156                )
157            }
158            SessionErrorKind::BandwidthExceeded { used, limit } => {
159                write!(f, "bandwidth exceeded: {} > {}", used, limit)
160            }
161        }
162    }
163}
164
165impl std::error::Error for RelayError {}
166
167impl From<std::io::Error> for RelayError {
168    fn from(error: std::io::Error) -> Self {
169        RelayError::NetworkError {
170            operation: "I/O operation".to_string(),
171            source: error.to_string(),
172        }
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn test_error_display() {
182        let auth_error = RelayError::AuthenticationFailed {
183            reason: "Invalid signature".to_string(),
184        };
185        assert!(auth_error.to_string().contains("Authentication failed"));
186
187        let rate_limit_error = RelayError::RateLimitExceeded {
188            retry_after_ms: 1000,
189        };
190        assert!(rate_limit_error.to_string().contains("Rate limit exceeded"));
191
192        let session_error = RelayError::SessionError {
193            session_id: Some(123),
194            kind: SessionErrorKind::NotFound,
195        };
196        assert!(session_error.to_string().contains("Session 123 error"));
197    }
198
199    #[test]
200    fn test_session_error_kind_display() {
201        let invalid_state = SessionErrorKind::InvalidState {
202            current_state: "Connected".to_string(),
203            expected_state: "Idle".to_string(),
204        };
205        assert!(invalid_state.to_string().contains("invalid state"));
206        assert!(invalid_state.to_string().contains("Connected"));
207        assert!(invalid_state.to_string().contains("Idle"));
208    }
209
210    #[test]
211    fn test_error_conversion() {
212        let io_error =
213            std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "Connection refused");
214        let relay_error: RelayError = io_error.into();
215
216        match relay_error {
217            RelayError::NetworkError { operation, source } => {
218                assert_eq!(operation, "I/O operation");
219                assert!(source.contains("Connection refused"));
220            }
221            _ => panic!("Expected NetworkError"),
222        }
223    }
224}