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("Type mismatch: expected '{expected}', got '{actual}'")]
45 ResponseTypeMismatch {
46 expected: String,
48 actual: String,
50 },
51}
52impl HttpError {
53 fn status_text(status: u16) -> &'static str {
54 match status {
55 200 => "OK",
56 400 => "Bad Request",
57 401 => "Unauthorized",
58 403 => "Forbidden",
59 404 => "Not Found",
60 405 => "Method Not Allowed",
61 408 => "Not Acceptable",
62 429 => "Too Many Requests",
63 500 => "Internal Server Error",
64 503 => "Service Unavailable",
65 504 => "Gateway Timeout",
66 _ => "Unknown",
67 }
68 }
69
70 #[allow(clippy::trivially_copy_pass_by_ref)]
72 fn format_http_error(status: &u16, message: &str) -> String {
73 if message.trim().is_empty() {
74 format!("HTTP {status} {}", Self::status_text(*status))
75 } else {
76 format!("HTTP {status}: {message}")
77 }
78 }
79}
80
81pub type HttpResult<T> = Result<T, HttpError>;