/ radicle-httpd / src / axum_extra.rs
axum_extra.rs
 1  use axum::extract::path::ErrorKind;
 2  use axum::extract::rejection::{PathRejection, QueryRejection};
 3  use axum::extract::FromRequestParts;
 4  use axum::http::request::Parts;
 5  use axum::http::StatusCode;
 6  use axum::{async_trait, Json};
 7  
 8  use serde::de::DeserializeOwned;
 9  use serde::Serialize;
10  
11  pub struct Path<T>(pub T);
12  
13  #[async_trait]
14  impl<S, T> FromRequestParts<S> for Path<T>
15  where
16      T: DeserializeOwned + Send,
17      S: Send + Sync,
18  {
19      type Rejection = (StatusCode, axum::Json<Error>);
20  
21      async fn from_request_parts(req: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
22          match axum::extract::Path::<T>::from_request_parts(req, state).await {
23              Ok(value) => Ok(Self(value.0)),
24              Err(rejection) => {
25                  let status = StatusCode::BAD_REQUEST;
26                  let body = match rejection {
27                      PathRejection::FailedToDeserializePathParams(inner) => {
28                          let kind = inner.into_kind();
29                          match &kind {
30                              ErrorKind::Message(msg) => Json(Error {
31                                  success: false,
32                                  error: msg.to_string(),
33                              }),
34                              _ => Json(Error {
35                                  success: false,
36                                  error: kind.to_string(),
37                              }),
38                          }
39                      }
40                      _ => Json(Error {
41                          success: false,
42                          error: format!("{rejection}"),
43                      }),
44                  };
45  
46                  Err((status, body))
47              }
48          }
49      }
50  }
51  
52  #[derive(Default)]
53  pub struct Query<T>(pub T);
54  
55  #[async_trait]
56  impl<S, T> FromRequestParts<S> for Query<T>
57  where
58      T: DeserializeOwned + Send,
59      S: Send + Sync,
60  {
61      type Rejection = (StatusCode, axum::Json<Error>);
62  
63      async fn from_request_parts(req: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
64          match axum::extract::Query::<T>::from_request_parts(req, state).await {
65              Ok(value) => Ok(Self(value.0)),
66              Err(rejection) => {
67                  let status = StatusCode::BAD_REQUEST;
68                  let body = match rejection {
69                      QueryRejection::FailedToDeserializeQueryString(inner) => Json(Error {
70                          success: false,
71                          error: inner.to_string(),
72                      }),
73                      _ => Json(Error {
74                          success: false,
75                          error: format!("{rejection}"),
76                      }),
77                  };
78  
79                  Err((status, body))
80              }
81          }
82      }
83  }
84  
85  #[derive(Serialize)]
86  pub struct Error {
87      success: bool,
88      error: String,
89  }