#[derive(thiserror::Error, Debug)]
pub enum HttpError {
#[error("Reqwest failure: {0}")]
RequestError(#[from] reqwest::Error),
#[error("Invalid URL: {0}")]
UrlParseError(#[from] url::ParseError),
#[error("JSON parsing failed: {0}")]
JsonError(#[from] serde_json::Error),
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
#[error("{}", HttpError::format_http_error(status, message))]
HttpStatus {
status: u16,
message: String,
},
#[error("{0}")]
ApiError(String),
#[error("TLS error: {0}")]
TLSError(String),
#[error("Missing 'response' field in API response")]
MissingResponseField,
#[error("Type mismatch: expected '{expected}', got '{actual}'")]
ResponseTypeMismatch {
expected: String,
actual: String,
},
}
impl HttpError {
fn status_text(status: u16) -> &'static str {
match status {
200 => "OK",
400 => "Bad Request",
401 => "Unauthorized",
403 => "Forbidden",
404 => "Not Found",
405 => "Method Not Allowed",
408 => "Not Acceptable",
429 => "Too Many Requests",
500 => "Internal Server Error",
503 => "Service Unavailable",
504 => "Gateway Timeout",
_ => "Unknown",
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn format_http_error(status: &u16, message: &str) -> String {
if message.trim().is_empty() {
format!("HTTP {status} {}", Self::status_text(*status))
} else {
format!("HTTP {status}: {message}")
}
}
}
pub type HttpResult<T> = Result<T, HttpError>;