use std::fmt;
#[derive(Debug)]
pub struct ApiError {
pub status: u16,
pub message: String,
pub body: String,
}
#[derive(Debug)]
pub enum Error {
Authentication(ApiError),
InvalidRequest(ApiError),
UnprocessableEntity(ApiError),
RateLimit(ApiError),
Api(ApiError),
Connection(String),
}
impl Error {
pub fn is_authentication(&self) -> bool {
matches!(self, Error::Authentication(_))
}
pub fn is_invalid_request(&self) -> bool {
matches!(self, Error::InvalidRequest(_))
}
pub fn is_unprocessable_entity(&self) -> bool {
matches!(self, Error::UnprocessableEntity(_))
}
pub fn is_rate_limit(&self) -> bool {
matches!(self, Error::RateLimit(_))
}
pub fn is_connection(&self) -> bool {
matches!(self, Error::Connection(_))
}
pub fn api_error(&self) -> Option<&ApiError> {
match self {
Error::Authentication(e)
| Error::InvalidRequest(e)
| Error::UnprocessableEntity(e)
| Error::RateLimit(e)
| Error::Api(e) => Some(e),
Error::Connection(_) => None,
}
}
pub fn status(&self) -> Option<u16> {
self.api_error().map(|e| e.status)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Authentication(e) => write!(f, "{}", e.message),
Error::InvalidRequest(e) => write!(f, "{}", e.message),
Error::UnprocessableEntity(e) => write!(f, "{}", e.message),
Error::RateLimit(e) => write!(f, "{}", e.message),
Error::Api(e) => write!(f, "{}", e.message),
Error::Connection(msg) => write!(f, "{}", msg),
}
}
}
impl std::error::Error for Error {}
pub fn new_api_error(status: u16, message: String, body: String) -> Error {
let api_error = ApiError {
status,
message,
body,
};
match status {
401 => Error::Authentication(api_error),
400 | 404 => Error::InvalidRequest(api_error),
422 => Error::UnprocessableEntity(api_error),
429 => Error::RateLimit(api_error),
_ => Error::Api(api_error),
}
}
pub fn new_connection_error(message: impl Into<String>) -> Error {
Error::Connection(format!(
"Could not connect to the Emailit API: {}",
message.into()
))
}