1use crate::{api_error::ApiError, json::JsonError, response::StatusCode};
2use std::{
3 error::Error as StdError,
4 fmt::{Debug, Display, Formatter, Result as FmtResult},
5 str,
6};
7
8#[derive(Debug)]
9pub struct Error {
10 pub(super) kind: ErrorType,
11 pub(super) source: Option<Box<dyn StdError + Send + Sync>>,
12}
13
14impl Error {
15 #[must_use = "retrieving the type has no effect if left unused"]
17 pub const fn kind(&self) -> &ErrorType {
18 &self.kind
19 }
20
21 #[must_use = "consuming the error and retrieving the source has no effect if left unused"]
23 pub fn into_source(self) -> Option<Box<dyn StdError + Send + Sync>> {
24 self.source
25 }
26
27 #[must_use = "consuming the error into its parts has no effect if left unused"]
29 pub fn into_parts(self) -> (ErrorType, Option<Box<dyn StdError + Send + Sync>>) {
30 (self.kind, self.source)
31 }
32
33 pub(super) fn json(source: JsonError) -> Self {
34 Self {
35 kind: ErrorType::Json,
36 source: Some(Box::new(source)),
37 }
38 }
39
40 pub(super) fn validation(source: impl StdError + Send + Sync + 'static) -> Self {
41 Self {
42 kind: ErrorType::Validation,
43 source: Some(Box::new(source)),
44 }
45 }
46}
47
48impl Display for Error {
49 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
50 match &self.kind {
51 ErrorType::BuildingRequest => f.write_str("failed to build the request"),
52 ErrorType::CreatingHeader { name, .. } => {
53 f.write_str("parsing the value for header ")?;
54 f.write_str(name)?;
55
56 f.write_str(" failed")
57 }
58 ErrorType::Json => f.write_str("given value couldn't be serialized"),
59 ErrorType::Parsing { body, .. } => {
60 f.write_str("response body couldn't be deserialized: ")?;
61
62 if let Ok(body) = str::from_utf8(body) {
63 f.write_str(body)
64 } else {
65 Debug::fmt(body, f)
66 }
67 }
68 ErrorType::RequestCanceled => {
69 f.write_str("request was canceled either before or while being sent")
70 }
71 ErrorType::RequestError => f.write_str("parsing or receiving the response failed"),
72 ErrorType::RequestTimedOut => f.write_str("request timed out"),
73 ErrorType::Response { body, status, .. } => {
74 f.write_str("response error: status code ")?;
75 Display::fmt(status, f)?;
76 f.write_str(", error: ")?;
77
78 f.write_str(&String::from_utf8_lossy(body))
79 }
80 ErrorType::Unauthorized => {
81 f.write_str("token in use is invalid, expired, or is revoked")
82 }
83 ErrorType::Validation => f.write_str("request fields have invalid values"),
84 }
85 }
86}
87
88impl StdError for Error {
89 fn source(&self) -> Option<&(dyn StdError + 'static)> {
90 self.source
91 .as_ref()
92 .map(|source| &**source as &(dyn StdError + 'static))
93 }
94}
95
96#[non_exhaustive]
98pub enum ErrorType {
99 BuildingRequest,
100 CreatingHeader {
101 name: String,
102 },
103 Json,
104 Parsing {
105 body: Vec<u8>,
106 },
107 RequestCanceled,
108 RequestError,
109 RequestTimedOut,
110 Response {
111 body: Vec<u8>,
112 error: ApiError,
113 status: StatusCode,
114 },
115 Unauthorized,
120 Validation,
177}
178
179impl Debug for ErrorType {
180 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
181 match self {
182 Self::BuildingRequest => f.write_str("BuildingRequest"),
183 Self::CreatingHeader { name } => f
184 .debug_struct("CreatingHeader")
185 .field("name", name)
186 .finish(),
187 Self::Json => f.write_str("Json"),
188 Self::Parsing { body } => {
189 let mut debug = f.debug_struct("Parsing");
190
191 if let Ok(body_string) = str::from_utf8(body) {
192 debug.field("body_string", &body_string);
193 }
194
195 debug.field("body", body).finish()
196 }
197 Self::RequestCanceled => f.write_str("RequestCanceled"),
198 Self::RequestError => f.write_str("RequestError"),
199 Self::RequestTimedOut => f.write_str("RequestTimedOut"),
200 Self::Response {
201 body,
202 error,
203 status,
204 } => {
205 let mut debug = f.debug_struct("Response");
206
207 if let Ok(body_string) = str::from_utf8(body) {
208 debug.field("body_string", &body_string);
209 }
210
211 debug
212 .field("body", body)
213 .field("error", error)
214 .field("status", status)
215 .finish()
216 }
217 Self::Unauthorized => f.write_str("Unauthorized"),
218 Self::Validation => f.write_str("Validation"),
219 }
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::ErrorType;
226 use crate::{
227 api_error::{ApiError, GeneralApiError},
228 response::StatusCode,
229 };
230
231 #[test]
233 fn parsing_variant_debug() {
234 let body = br#"{"message": "aaa"#.to_vec();
235
236 let error = ErrorType::Parsing { body };
237
238 assert_eq!(
239 "Parsing {
240 body_string: \"{\\\"message\\\": \\\"aaa\",
241 body: [
242 123,
243 34,
244 109,
245 101,
246 115,
247 115,
248 97,
249 103,
250 101,
251 34,
252 58,
253 32,
254 34,
255 97,
256 97,
257 97,
258 ],
259}",
260 format!("{error:#?}"),
261 );
262 }
263
264 #[test]
265 fn response_variant_debug() {
266 let body = br#"{"message": "aaa"}"#.to_vec();
267
268 let error = ErrorType::Response {
269 body,
270 error: ApiError::General(GeneralApiError {
271 code: 0,
272 message: "401: Unauthorized".to_owned(),
273 }),
274 status: StatusCode::new(401),
275 };
276
277 assert_eq!(
278 "Response {
279 body_string: \"{\\\"message\\\": \\\"aaa\\\"}\",
280 body: [
281 123,
282 34,
283 109,
284 101,
285 115,
286 115,
287 97,
288 103,
289 101,
290 34,
291 58,
292 32,
293 34,
294 97,
295 97,
296 97,
297 34,
298 125,
299 ],
300 error: General(
301 GeneralApiError {
302 code: 0,
303 message: \"401: Unauthorized\",
304 },
305 ),
306 status: StatusCode(
307 401,
308 ),
309}",
310 format!("{error:#?}"),
311 );
312 }
313}