use crate::error::Error;
use axum::Json;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde_derive::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
#[serde_with::skip_serializing_none]
#[derive(Debug, Error, Serialize, Deserialize)]
#[cfg_attr(feature = "open-api", derive(aide::OperationIo, schemars::JsonSchema))]
#[non_exhaustive]
pub struct HttpError {
#[serde(skip)]
pub status: StatusCode,
pub error: Option<String>,
pub details: Option<String>,
#[source]
#[serde(skip)]
pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
}
impl Display for HttpError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Http Error {} - {:?}", self.status, self.error)
}
}
impl HttpError {
pub fn new(status: StatusCode) -> Self {
Self {
status,
error: None,
details: None,
source: None,
}
}
pub fn to_err(self) -> Error {
self.into()
}
pub fn error(self, error: impl ToString) -> Self {
Self {
error: Some(error.to_string()),
..self
}
}
pub fn details(self, details: impl ToString) -> Self {
Self {
details: Some(details.to_string()),
..self
}
}
pub fn source(self, source: impl std::error::Error + Send + Sync + 'static) -> Self {
Self {
source: Some(Box::new(source)),
..self
}
}
pub fn bad_request() -> Self {
Self::new(StatusCode::BAD_REQUEST)
}
pub fn unauthorized() -> Self {
Self::new(StatusCode::UNAUTHORIZED)
}
pub fn forbidden() -> Self {
Self::new(StatusCode::FORBIDDEN)
}
pub fn not_found() -> Self {
Self::new(StatusCode::NOT_FOUND)
}
pub fn gone() -> Self {
Self::new(StatusCode::GONE)
}
pub fn internal_server_error() -> Self {
Self::new(StatusCode::INTERNAL_SERVER_ERROR)
}
}
impl From<StatusCode> for HttpError {
fn from(value: StatusCode) -> Self {
HttpError::new(value)
}
}
impl From<StatusCode> for Error {
fn from(value: StatusCode) -> Self {
HttpError::new(value).into()
}
}
impl IntoResponse for HttpError {
fn into_response(self) -> Response {
let status = self.status;
let mut res = Json(self).into_response();
*res.status_mut() = status;
res
}
}