1use std::fmt;
4
5pub type RelayResult<T> = Result<T, RelayError>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum RelayError {
11 AuthenticationFailed { reason: String },
13
14 RateLimitExceeded { retry_after_ms: u64 },
16
17 SessionError {
19 session_id: Option<u32>,
20 kind: SessionErrorKind,
21 },
22
23 NetworkError { operation: String, source: String },
25
26 ProtocolError { frame_type: u8, reason: String },
28
29 ResourceExhausted {
31 resource_type: String,
32 current_usage: u64,
33 limit: u64,
34 },
35
36 ConfigurationError { parameter: String, reason: String },
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum SessionErrorKind {
43 NotFound,
45 AlreadyExists,
47 Expired,
49 Terminated,
51 InvalidState {
53 current_state: String,
54 expected_state: String,
55 },
56 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}