1use axum::{
2 Json,
3 http::{StatusCode, header::InvalidHeaderValue},
4 response::{IntoResponse, Response},
5};
6use serde::Serialize;
7use std::{
8 error::Error,
9 fmt::{Debug, Display},
10};
11use thiserror::Error;
12use utoipa::ToSchema;
13
14pub type HttpResult<T> = Result<Json<T>, DynHttpError>;
16
17pub type HttpStatusResult = Result<StatusCode, DynHttpError>;
18
19pub struct DynHttpError {
21 inner: Box<dyn HttpError>,
23}
24
25impl Debug for DynHttpError {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_tuple(self.inner.type_name())
28 .field(&self.inner)
29 .finish()
30 }
31}
32
33impl Display for DynHttpError {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 Display::fmt(&self.inner, f)
36 }
37}
38
39impl Error for DynHttpError {}
40
41impl IntoResponse for DynHttpError {
43 fn into_response(self) -> Response {
44 let body = Json(HttpErrorResponse {
46 reason: self.inner.reason(),
47 });
48 let status = self.inner.status();
49
50 (status, body).into_response()
51 }
52}
53
54pub trait HttpError: Error + Send + Sync + 'static {
57 fn status(&self) -> StatusCode {
59 StatusCode::INTERNAL_SERVER_ERROR
60 }
61
62 fn reason(&self) -> String {
64 self.to_string()
65 }
66
67 fn type_name(&self) -> &str {
70 std::any::type_name::<Self>()
71 }
72}
73
74impl<E> From<E> for DynHttpError
76where
77 E: HttpError,
78{
79 fn from(value: E) -> Self {
80 DynHttpError {
81 inner: Box::new(value),
82 }
83 }
84}
85
86impl HttpError for axum::http::Error {
87 fn status(&self) -> StatusCode {
88 StatusCode::INTERNAL_SERVER_ERROR
89 }
90}
91
92impl HttpError for InvalidHeaderValue {
93 fn status(&self) -> StatusCode {
94 StatusCode::INTERNAL_SERVER_ERROR
95 }
96}
97
98#[derive(Debug, Serialize, ToSchema)]
100#[serde(rename_all = "camelCase")]
101pub struct HttpErrorResponse {
102 pub reason: String,
103}
104
105#[derive(Debug, Error)]
106pub enum HttpCommonError {
107 #[error("internal server error")]
108 ServerError,
109 #[error("unsupported endpoint")]
110 Unsupported,
111}
112
113impl HttpError for HttpCommonError {
114 fn status(&self) -> axum::http::StatusCode {
115 match self {
116 HttpCommonError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
117 HttpCommonError::Unsupported => StatusCode::NOT_IMPLEMENTED,
118 }
119 }
120}