metabase_api_rs/core/
error.rs1use thiserror::Error;
7
8#[derive(Debug, Error)]
10pub enum Error {
11 #[error("HTTP request failed with status {status}: {message}")]
13 Http { status: u16, message: String },
14
15 #[error("JSON parsing failed: {0}")]
17 Json(String),
18
19 #[error("Serialization error: {0}")]
21 Serialization(String),
22
23 #[error("Authentication failed: {0}")]
25 Authentication(String),
26
27 #[error("Validation error: {0}")]
29 Validation(String),
30
31 #[error("Configuration error: {0}")]
33 Config(String),
34
35 #[error("Session error: {0}")]
37 Session(String),
38
39 #[error("Invalid parameter: {0}")]
41 InvalidParameter(String),
42
43 #[error("Resource not found: {0}")]
45 NotFound(String),
46
47 #[error("Rate limited")]
49 RateLimited { retry_after: Option<u32> },
50
51 #[error("Server error: {0}")]
53 Server(String),
54
55 #[error("Request timeout")]
57 Timeout,
58
59 #[error("Network error: {0}")]
61 Network(String),
62
63 #[error("Not implemented: {0}")]
65 NotImplemented(String),
66
67 #[error("Unknown error: {0}")]
69 Unknown(String),
70}
71
72pub type Result<T> = std::result::Result<T, Error>;
74
75impl From<serde_json::Error> for Error {
77 fn from(err: serde_json::Error) -> Self {
78 Error::Json(err.to_string())
79 }
80}
81
82impl From<reqwest::Error> for Error {
83 fn from(err: reqwest::Error) -> Self {
84 if err.is_timeout() {
85 Error::Timeout
86 } else if err.is_connect() {
87 Error::Network(format!("Connection failed: {}", err))
88 } else if err.is_status() {
89 if let Some(status) = err.status() {
90 match status.as_u16() {
91 401 | 403 => Error::Authentication(format!("Authentication error: {}", status)),
92 404 => Error::NotFound(format!("Resource not found: {}", status)),
93 429 => Error::RateLimited { retry_after: None },
94 500..=599 => Error::Server(format!("Server error: {}", status)),
95 code => Error::Http {
96 status: code,
97 message: format!("HTTP error: {}", status),
98 },
99 }
100 } else {
101 Error::Http {
102 status: 0,
103 message: err.to_string(),
104 }
105 }
106 } else {
107 Error::Http {
108 status: 0,
109 message: err.to_string(),
110 }
111 }
112 }
113}
114
115impl From<std::io::Error> for Error {
116 fn from(err: std::io::Error) -> Self {
117 Error::Network(format!("IO error: {}", err))
118 }
119}
120
121#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_error_http_variant() {
128 let status = 400;
130 let msg = "Bad Request".to_string();
131
132 let error = Error::Http {
134 status,
135 message: msg.clone(),
136 };
137
138 assert_eq!(
140 error.to_string(),
141 format!("HTTP request failed with status {}: {}", status, msg)
142 );
143 }
144
145 #[test]
146 fn test_error_json_variant() {
147 let msg = "Invalid JSON structure".to_string();
149
150 let error = Error::Json(msg.clone());
152
153 assert_eq!(error.to_string(), format!("JSON parsing failed: {}", msg));
155 }
156
157 #[test]
158 fn test_error_auth_variant() {
159 let msg = "Invalid credentials".to_string();
161
162 let error = Error::Authentication(msg.clone());
164
165 assert_eq!(error.to_string(), format!("Authentication failed: {}", msg));
167 }
168
169 #[test]
170 fn test_error_not_found_variant() {
171 let msg = "Card with id 123".to_string();
173
174 let error = Error::NotFound(msg.clone());
176
177 assert_eq!(error.to_string(), format!("Resource not found: {}", msg));
179 }
180
181 #[test]
182 fn test_error_rate_limited() {
183 let error = Error::RateLimited { retry_after: None };
185
186 assert_eq!(error.to_string(), "Rate limited");
188 }
189
190 #[test]
191 fn test_error_from_reqwest_error() {
192 }
203
204 #[test]
205 fn test_error_from_serde_json_error() {
206 let json_err = serde_json::from_str::<String>("invalid json").unwrap_err();
208
209 let error: Error = json_err.into();
211
212 match error {
214 Error::Json(_) => (),
215 _ => panic!("Expected Json error"),
216 }
217 }
218
219 #[test]
220 fn test_result_type_alias() {
221 fn test_function() -> Result<String> {
223 Ok("success".to_string())
224 }
225
226 let result = test_function();
228
229 assert!(result.is_ok());
231 assert_eq!(result.unwrap(), "success");
232 }
233
234 #[test]
235 fn test_error_is_send_sync() {
236 fn assert_send_sync<T: Send + Sync>() {}
237 assert_send_sync::<Error>();
238 }
239}