use axum::extract::path::ErrorKind; use axum::extract::rejection::{PathRejection, QueryRejection}; use axum::extract::FromRequestParts; use axum::http::request::Parts; use axum::http::{header, StatusCode}; use axum::response::IntoResponse; use axum::{async_trait, Json}; use serde::de::DeserializeOwned; use serde::Serialize; pub struct Path(pub T); #[async_trait] impl FromRequestParts for Path where T: DeserializeOwned + Send, S: Send + Sync, { type Rejection = (StatusCode, axum::Json); async fn from_request_parts(req: &mut Parts, state: &S) -> Result { match axum::extract::Path::::from_request_parts(req, state).await { Ok(value) => Ok(Self(value.0)), Err(rejection) => { let status = StatusCode::BAD_REQUEST; let body = match rejection { PathRejection::FailedToDeserializePathParams(inner) => { let kind = inner.into_kind(); match &kind { ErrorKind::Message(msg) => Json(Error { success: false, error: msg.to_string(), }), _ => Json(Error { success: false, error: kind.to_string(), }), } } _ => Json(Error { success: false, error: format!("{rejection}"), }), }; Err((status, body)) } } } } #[derive(Default)] pub struct Query(pub T); #[async_trait] impl FromRequestParts for Query where T: DeserializeOwned + Send, S: Send + Sync, { type Rejection = (StatusCode, axum::Json); async fn from_request_parts(req: &mut Parts, state: &S) -> Result { match axum::extract::Query::::from_request_parts(req, state).await { Ok(value) => Ok(Self(value.0)), Err(rejection) => { let status = StatusCode::BAD_REQUEST; let body = match rejection { QueryRejection::FailedToDeserializeQueryString(inner) => Json(Error { success: false, error: inner.to_string(), }), _ => Json(Error { success: false, error: format!("{rejection}"), }), }; Err((status, body)) } } } } #[derive(Serialize)] pub struct Error { success: bool, error: String, } /// Add a Cache-Control header that marks the response as immutable and /// instructs clients to cache the response for 7 days. pub fn immutable_response(data: impl serde::Serialize) -> impl IntoResponse { ( [(header::CACHE_CONTROL, "public, max-age=604800, immutable")], Json(data), ) }