1use std::fmt;
11
12pub type RelayResult<T> = Result<T, RelayError>;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum RelayError {
18 AuthenticationFailed {
20 reason: String,
22 },
23
24 RateLimitExceeded {
26 retry_after_ms: u64,
28 },
29
30 SessionError {
32 session_id: Option<u32>,
34 kind: SessionErrorKind,
36 },
37
38 NetworkError {
40 operation: String,
42 source: String,
44 },
45
46 ProtocolError {
48 frame_type: u8,
50 reason: String,
52 },
53
54 ResourceExhausted {
56 resource_type: String,
58 current_usage: u64,
60 limit: u64,
62 },
63
64 ConfigurationError {
66 parameter: String,
68 reason: String,
70 },
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum SessionErrorKind {
76 NotFound,
78 AlreadyExists,
80 Expired,
82 Terminated,
84 InvalidState {
86 current_state: String,
88 expected_state: String,
90 },
91 BandwidthExceeded {
93 used: u64,
95 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}