use std::fmt;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ApiError {
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Not found: {0}")]
NotFound(ErrorResponse),
#[error("Bad request: {0}")]
BadRequest(ErrorResponse),
#[error("Permission denied: {0}")]
Forbidden(ErrorResponse),
#[error("Rate limited: {0}")]
RateLimited(ErrorResponse),
#[error("Unauthorized: {0}")]
Unauthorized(ErrorResponse),
#[error("Conflict: {0}")]
Conflict(ErrorResponse),
#[error("Server error: {0}")]
ServerError(ErrorResponse),
#[error("Deserialization error: {0}")]
Deserialize(String),
#[error("Invalid parameter: {0}")]
InvalidParameter(String),
#[error("Authentication required: call set_auth_token() or use builder().auth_token()")]
AuthenticationRequired,
#[error("Unexpected status {0}: {1}")]
UnexpectedStatus(u16, ErrorResponse),
}
impl ApiError {
pub fn error_response(&self) -> Option<&ErrorResponse> {
match self {
ApiError::NotFound(resp) => Some(resp),
ApiError::BadRequest(resp) => Some(resp),
ApiError::Forbidden(resp) => Some(resp),
ApiError::RateLimited(resp) => Some(resp),
ApiError::Unauthorized(resp) => Some(resp),
ApiError::Conflict(resp) => Some(resp),
ApiError::ServerError(resp) => Some(resp),
ApiError::UnexpectedStatus(_, resp) => Some(resp),
_ => None,
}
}
pub fn status_code(&self) -> Option<u16> {
match self {
ApiError::NotFound(_) => Some(404),
ApiError::BadRequest(_) => Some(400),
ApiError::Forbidden(_) => Some(403),
ApiError::RateLimited(_) => Some(429),
ApiError::Unauthorized(_) => Some(401),
ApiError::Conflict(_) => Some(409),
ApiError::ServerError(_) => Some(500),
ApiError::UnexpectedStatus(code, _) => Some(*code),
_ => None,
}
}
}
pub type ApiResult<T> = Result<T, ApiError>;
#[derive(Debug, Clone, serde::Deserialize)]
pub struct ErrorResponse {
#[serde(default)]
pub status: Option<String>,
#[serde(alias = "error")]
pub message: Option<String>,
#[serde(default)]
pub details: Option<String>,
}
impl ErrorResponse {
pub fn from_text(text: String) -> Self {
Self {
status: None,
message: Some(text),
details: None,
}
}
#[deprecated(note = "Use Display formatting instead")]
pub fn get_message(&self) -> String {
self.message
.clone()
.or_else(|| self.details.clone())
.unwrap_or_else(|| "Unknown error".to_string())
}
}
impl fmt::Display for ErrorResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (&self.message, &self.details) {
(Some(msg), Some(details)) => write!(f, "{}: {}", msg, details),
(Some(msg), None) => write!(f, "{}", msg),
(None, Some(details)) => write!(f, "{}", details),
(None, None) => write!(f, "Unknown error"),
}
}
}