1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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())
            }
          }
        }
      }
    }
  };
}