1use std::fmt;
4
5pub type RelayResult<T> = Result<T, RelayError>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum RelayError {
11 AuthenticationFailed {
13 reason: String,
14 },
15
16 RateLimitExceeded {
18 retry_after_ms: u64,
19 },
20
21 SessionError {
23 session_id: Option<u32>,
24 kind: SessionErrorKind,
25 },
26
27 NetworkError {
29 operation: String,
30 source: String,
31 },
32
33 ProtocolError {
35 frame_type: u8,
36 reason: String,
37 },
38
39 ResourceExhausted {
41 resource_type: String,
42 current_usage: u64,
43 limit: u64,
44 },
45
46 ConfigurationError {
48 parameter: String,
49 reason: String,
50 },
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum SessionErrorKind {
56 NotFound,
58 AlreadyExists,
60 Expired,
62 Terminated,
64 InvalidState {
66 current_state: String,
67 expected_state: String,
68 },
69 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}