1#[derive(thiserror::Error, Debug)]
5pub enum HttpError {
6 #[error("Reqwest failure: {0}")]
8 RequestError(#[from] reqwest::Error),
9
10 #[error("Invalid URL: {0}")]
12 UrlParseError(#[from] url::ParseError),
13
14 #[error("JSON parsing failed: {0}")]
16 JsonError(#[from] serde_json::Error),
17
18 #[error("IO error: {0}")]
20 IOError(#[from] std::io::Error),
21
22 #[error("{}", HttpError::format_http_error(status, message))]
24 HttpStatus {
25 status: u16,
27 message: String,
29 },
30
31 #[error("{0}")]
33 ApiError(String),
34
35 #[error("TLS error: {0}")]
37 TLSError(String),
38
39 #[error("Missing 'response' field in API response")]
41 MissingResponseField,
42
43 #[error("Missing 'type' field in API response")]
45 MissingTypeField,
46
47 #[error("Missing 'data' field in API response")]
49 MissingDataField,
50
51 #[error("Type mismatch: expected '{expected}', got '{actual}'")]
53 ResponseTypeMismatch {
54 expected: String,
56 actual: String,
58 },
59}
60impl HttpError {
61 fn status_text(status: u16) -> &'static str {
62 match status {
63 200 => "OK",
64 400 => "Bad Request",
65 401 => "Unauthorized",
66 403 => "Forbidden",
67 404 => "Not Found",
68 405 => "Method Not Allowed",
69 408 => "Not Acceptable",
70 429 => "Too Many Requests",
71 500 => "Internal Server Error",
72 503 => "Service Unavailable",
73 504 => "Gateway Timeout",
74 _ => "Unknown",
75 }
76 }
77
78 #[allow(clippy::trivially_copy_pass_by_ref)]
80 fn format_http_error(status: &u16, message: &str) -> String {
81 if message.trim().is_empty() {
82 format!("HTTP {status} {}", Self::status_text(*status))
83 } else {
84 format!("HTTP {status}: {message}")
85 }
86 }
87}
88
89pub type HttpResult<T> = Result<T, HttpError>;