use std::fmt;
pub use anyhow;
use axum::body::{Body, Bytes};
pub use axum::{
async_trait,
extract::FromRequestParts,
http::{request::Parts, StatusCode},
response::{IntoResponse, Response},
};
#[derive(Debug, Clone)]
pub struct ErrorResponse(Box<(StatusCode, Bytes)>);
impl std::error::Error for ErrorResponse {}
impl fmt::Display for ErrorResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
pub fn error_response<T>(
code: StatusCode,
msg: impl Into<Bytes>,
) -> anyhow::Result<T, ErrorResponse> {
Err(ErrorResponse((code, msg.into()).into()))
}
impl IntoResponse for ErrorResponse {
fn into_response(self) -> Response<Body> {
self.0.into_response()
}
}
#[macro_export]
macro_rules! from_request_parts {
($cls:ty, $func:expr) => {
#[$crate::async_trait]
impl<S> $crate::FromRequestParts<S> for $cls
where
S: Send + Sync,
{
type Rejection = $crate::Response;
async fn from_request_parts(
parts: &mut $crate::Parts,
_state: &S,
) -> Result<Self, Self::Rejection> {
use std::sync::Arc;
use $crate::{anyhow, IntoResponse};
let func = $func;
let r: anyhow::Result<Self> = func(parts).await;
match r {
Ok(r) => Ok(r),
Err(error) => {
if let Some(r) = error.downcast_ref::<Box<$crate::ErrorResponse>>() {
Err(r.clone().into_response())
} else {
let error = error.to_string();
tracing::error!("{}", error);
Err(($crate::StatusCode::INTERNAL_SERVER_ERROR, error).into_response())
}
}
}
}
}
};
}