use axum::extract::rejection::{FormRejection, JsonRejection, PathRejection, QueryRejection}; use axum::extract::{FromRequest, FromRequestParts, Path, Query}; use axum::http::request::Parts; use axum::http::{Request, StatusCode}; use axum::response::{IntoResponse, Response}; use axum::{async_trait, Form, Json}; use validator::{Validate, ValidationErrors}; #[derive(Debug, Clone, Copy, Default)] pub struct Valid(pub T); pub enum ValidRejection { Valid(ValidationErrors), Inner(E), } impl From for ValidRejection { fn from(value: ValidationErrors) -> Self { Self::Valid(value) } } impl IntoResponse for ValidRejection { fn into_response(self) -> Response { match self { ValidRejection::Valid(validate_error) => { (StatusCode::BAD_REQUEST, validate_error.to_string()).into_response() } ValidRejection::Inner(json_error) => json_error.into_response(), } } } impl From for ValidRejection { fn from(value: JsonRejection) -> Self { Self::Inner(value) } } impl From for ValidRejection { fn from(value: QueryRejection) -> Self { Self::Inner(value) } } impl From for ValidRejection { fn from(value: PathRejection) -> Self { Self::Inner(value) } } impl From for ValidRejection { fn from(value: FormRejection) -> Self { Self::Inner(value) } } pub trait HasValidate { type Validate: Validate; type Rejection; fn get_validate(&self) -> &Self::Validate; } impl HasValidate for Json { type Validate = T; type Rejection = JsonRejection; fn get_validate(&self) -> &T { &self.0 } } impl HasValidate for Form { type Validate = T; type Rejection = FormRejection; fn get_validate(&self) -> &T { &self.0 } } impl HasValidate for Query { type Validate = T; type Rejection = QueryRejection; fn get_validate(&self) -> &T { &self.0 } } impl HasValidate for Path { type Validate = T; type Rejection = QueryRejection; fn get_validate(&self) -> &T { &self.0 } } #[async_trait] impl FromRequest for Valid where S: Send + Sync + 'static, B: Send + Sync + 'static, T: HasValidate + FromRequest, T::Validate: Validate, ::Rejection: IntoResponse, ValidRejection<::Rejection>: From<>::Rejection>, { type Rejection = ValidRejection<::Rejection>; async fn from_request(req: Request, state: &S) -> Result { let valid = T::from_request(req, state).await?; valid.get_validate().validate()?; Ok(Valid(valid)) } } #[async_trait] impl FromRequestParts for Valid where S: Send + Sync + 'static, T: HasValidate + FromRequestParts, T::Validate: Validate, ::Rejection: IntoResponse, ValidRejection<::Rejection>: From<>::Rejection>, { type Rejection = ValidRejection<::Rejection>; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { let valid = T::from_request_parts(parts, state).await?; valid.get_validate().validate()?; Ok(Valid(valid)) } }