use ntex::web;
use ntex::http;
#[derive(Clone, Debug)]
pub struct HttpError {
pub msg: String,
pub status: http::StatusCode,
}
impl HttpError {
pub fn new<T>(status: http::StatusCode, msg: T) -> Self
where
T: ToString,
{
Self {
status,
msg: msg.to_string(),
}
}
pub fn bad_request<T>(msg: T) -> Self
where
T: ToString,
{
Self::new(http::StatusCode::BAD_REQUEST, msg)
}
pub fn unauthorized<T>(msg: T) -> Self
where
T: ToString,
{
Self::new(http::StatusCode::UNAUTHORIZED, msg)
}
pub fn forbidden<T>(msg: T) -> Self
where
T: ToString,
{
Self::new(http::StatusCode::FORBIDDEN, msg)
}
pub fn not_found<T>(msg: T) -> Self
where
T: ToString,
{
Self::new(http::StatusCode::NOT_FOUND, msg)
}
pub fn internal_server_error<T>(msg: T) -> Self
where
T: ToString,
{
Self::new(http::StatusCode::INTERNAL_SERVER_ERROR, msg)
}
pub fn conflict<T>(msg: T) -> Self
where
T: ToString,
{
Self::new(http::StatusCode::CONFLICT, msg)
}
pub fn bad_gateway<T>(msg: T) -> Self
where
T: ToString,
{
Self::new(http::StatusCode::BAD_GATEWAY, msg)
}
}
impl std::fmt::Display for HttpError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}] {}", self.status, self.msg)
}
}
impl std::error::Error for HttpError {}
impl web::WebResponseError for HttpError {
fn error_response(&self, _: &web::HttpRequest) -> web::HttpResponse {
let err_json = serde_json::json!({ "msg": self.msg });
web::HttpResponse::build(self.status).json(&err_json)
}
}
#[cfg(feature = "io_error")]
impl From<crate::io_error::IoError> for HttpError {
fn from(err: crate::io_error::IoError) -> Self {
match err.inner.kind() {
std::io::ErrorKind::NotFound => HttpError::not_found(err.to_string()),
std::io::ErrorKind::AlreadyExists => HttpError::conflict(err.to_string()),
std::io::ErrorKind::ConnectionAborted => {
HttpError::bad_gateway(err.to_string())
}
std::io::ErrorKind::ConnectionRefused => {
HttpError::bad_gateway(err.to_string())
}
std::io::ErrorKind::InvalidData => {
HttpError::bad_request(err.to_string())
}
_ => HttpError::internal_server_error(err.to_string()),
}
}
}
#[cfg(feature = "io_error")]
impl From<Box<crate::io_error::IoError>> for HttpError {
fn from(err: Box<crate::io_error::IoError>) -> Self {
(*err).into()
}
}
#[cfg(feature = "bollard")]
impl From<bollard_next::errors::Error> for HttpError {
fn from(err: bollard_next::errors::Error) -> Self {
match err {
bollard_next::errors::Error::DockerResponseServerError {
status_code,
message,
} => HttpError {
msg: message,
status: http::StatusCode::from_u16(status_code)
.unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR),
},
_ => HttpError {
msg: format!("{err}"),
status: http::StatusCode::INTERNAL_SERVER_ERROR,
},
}
}
}
#[cfg(feature = "io_error")]
impl crate::io_error::FromIo<HttpError> for HttpError {
fn map_err_context<C>(self, context: impl FnOnce() -> C) -> HttpError
where
C: ToString + std::fmt::Display,
{
HttpError::new(self.status, format!("{}: {}", context(), self.msg))
}
}