contextlite_client/
error.rs1use thiserror::Error;
7
8#[derive(Error, Debug)]
10pub enum ContextLiteError {
11 #[error("HTTP request failed: {0}")]
13 HttpError(#[from] reqwest::Error),
14
15 #[error("JSON processing failed: {0}")]
17 JsonError(#[from] serde_json::Error),
18
19 #[error("Invalid URL: {0}")]
21 UrlError(#[from] url::ParseError),
22
23 #[error("Authentication failed: {message}")]
25 AuthError {
26 message: String
28 },
29
30 #[error("Server error {status}: {message}")]
32 ServerError {
33 status: u16,
35 message: String
37 },
38
39 #[error("Document validation failed: {message}")]
41 ValidationError {
42 message: String
44 },
45
46 #[error("Configuration error: {message}")]
48 ConfigError {
49 message: String
51 },
52
53 #[error("Request timed out after {seconds} seconds")]
55 TimeoutError {
56 seconds: u64
58 },
59
60 #[error("Connection pool error: {message}")]
62 ConnectionError {
63 message: String
65 },
66
67 #[error("Invalid response format: {message}")]
69 ResponseError {
70 message: String
72 },
73
74 #[error("Rate limit exceeded: {message}")]
76 RateLimitError {
77 message: String
79 },
80
81 #[error("Client error: {message}")]
83 ClientError {
84 message: String
86 },
87}
88
89impl ContextLiteError {
90 pub fn auth(message: impl Into<String>) -> Self {
92 Self::AuthError {
93 message: message.into(),
94 }
95 }
96
97 pub fn server(status: u16, message: impl Into<String>) -> Self {
99 Self::ServerError {
100 status,
101 message: message.into(),
102 }
103 }
104
105 pub fn validation(message: impl Into<String>) -> Self {
107 Self::ValidationError {
108 message: message.into(),
109 }
110 }
111
112 pub fn config(message: impl Into<String>) -> Self {
114 Self::ConfigError {
115 message: message.into(),
116 }
117 }
118
119 pub fn timeout(seconds: u64) -> Self {
121 Self::TimeoutError { seconds }
122 }
123
124 pub fn connection(message: impl Into<String>) -> Self {
126 Self::ConnectionError {
127 message: message.into(),
128 }
129 }
130
131 pub fn response(message: impl Into<String>) -> Self {
133 Self::ResponseError {
134 message: message.into(),
135 }
136 }
137
138 pub fn rate_limit(message: impl Into<String>) -> Self {
140 Self::RateLimitError {
141 message: message.into(),
142 }
143 }
144
145 pub fn client(message: impl Into<String>) -> Self {
147 Self::ClientError {
148 message: message.into(),
149 }
150 }
151
152 pub fn is_retryable(&self) -> bool {
154 match self {
155 Self::HttpError(e) => {
156 e.is_connect() || e.is_timeout()
158 }
159 Self::ServerError { status, .. } => {
160 *status >= 500
162 }
163 Self::TimeoutError { .. } => true,
164 Self::ConnectionError { .. } => true,
165 Self::RateLimitError { .. } => true,
166 _ => false,
167 }
168 }
169
170 pub fn is_client_error(&self) -> bool {
172 match self {
173 Self::ValidationError { .. } |
174 Self::ConfigError { .. } |
175 Self::AuthError { .. } |
176 Self::JsonError(_) |
177 Self::UrlError(_) => true,
178 Self::ServerError { status, .. } => *status >= 400 && *status < 500,
179 _ => false,
180 }
181 }
182
183 pub fn is_server_error(&self) -> bool {
185 match self {
186 Self::ServerError { status, .. } => *status >= 500,
187 _ => false,
188 }
189 }
190}
191
192pub type Result<T> = std::result::Result<T, ContextLiteError>;
194
195pub async fn handle_response_error(response: reqwest::Response) -> Result<reqwest::Response> {
197 let status = response.status();
198
199 if status.is_success() {
200 Ok(response)
201 } else if status == 401 {
202 Err(ContextLiteError::auth("Invalid or missing authentication token"))
203 } else if status == 429 {
204 let message = response.text().await
205 .unwrap_or_else(|_| "Rate limit exceeded".to_string());
206 Err(ContextLiteError::rate_limit(message))
207 } else {
208 let message = response.text().await
209 .unwrap_or_else(|_| format!("HTTP {}", status.as_u16()));
210 Err(ContextLiteError::server(status.as_u16(), message))
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_error_creation() {
220 let auth_err = ContextLiteError::auth("Invalid token");
221 assert!(matches!(auth_err, ContextLiteError::AuthError { .. }));
222 assert!(auth_err.is_client_error());
223 assert!(!auth_err.is_retryable());
224
225 let server_err = ContextLiteError::server(500, "Internal error");
226 assert!(matches!(server_err, ContextLiteError::ServerError { .. }));
227 assert!(server_err.is_server_error());
228 assert!(server_err.is_retryable());
229
230 let timeout_err = ContextLiteError::timeout(30);
231 assert!(matches!(timeout_err, ContextLiteError::TimeoutError { .. }));
232 assert!(timeout_err.is_retryable());
233 }
234
235 #[test]
236 fn test_error_display() {
237 let auth_err = ContextLiteError::auth("Invalid token");
238 assert_eq!(auth_err.to_string(), "Authentication failed: Invalid token");
239
240 let server_err = ContextLiteError::server(404, "Not found");
241 assert_eq!(server_err.to_string(), "Server error 404: Not found");
242
243 let timeout_err = ContextLiteError::timeout(60);
244 assert_eq!(timeout_err.to_string(), "Request timed out after 60 seconds");
245 }
246
247 #[test]
248 fn test_retryable_classification() {
249 assert!(ContextLiteError::server(500, "error").is_retryable());
251 assert!(ContextLiteError::server(502, "error").is_retryable());
252 assert!(ContextLiteError::timeout(30).is_retryable());
253 assert!(ContextLiteError::connection("error").is_retryable());
254 assert!(ContextLiteError::rate_limit("error").is_retryable());
255
256 assert!(!ContextLiteError::server(400, "error").is_retryable());
258 assert!(!ContextLiteError::server(404, "error").is_retryable());
259 assert!(!ContextLiteError::auth("error").is_retryable());
260 assert!(!ContextLiteError::validation("error").is_retryable());
261 }
262
263 #[test]
264 fn test_error_classification() {
265 assert!(ContextLiteError::server(400, "error").is_client_error());
267 assert!(ContextLiteError::server(404, "error").is_client_error());
268 assert!(ContextLiteError::auth("error").is_client_error());
269 assert!(ContextLiteError::validation("error").is_client_error());
270
271 assert!(ContextLiteError::server(500, "error").is_server_error());
273 assert!(ContextLiteError::server(502, "error").is_server_error());
274
275 assert!(!ContextLiteError::timeout(30).is_client_error());
277 assert!(!ContextLiteError::timeout(30).is_server_error());
278 }
279}