//! # Validify support //! //! ## Feature //! //! Enable the `validify` feature to use `Validated`, `Modified` and `Validified`. //! #[cfg(test)] pub mod test; use crate::{HasValidate, ValidationRejection}; use axum::async_trait; use axum::extract::{FromRequest, FromRequestParts}; use axum::http::request::Parts; use axum::http::Request; use axum::response::{IntoResponse, Response}; use std::fmt::{Display, Formatter}; use std::ops::{Deref, DerefMut}; use validify::{Modify, Validate, ValidationErrors, Validify}; /// # `Validated` data extractor /// #[derive(Debug, Clone, Copy, Default)] pub struct Validated(pub E); impl Deref for Validated { type Target = E; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Validated { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Display for Validated { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } impl Validated { /// Consumes the `Validified` and returns the validated data within. /// /// This returns the `E` type which represents the data that has been /// successfully validated. pub fn into_inner(self) -> E { self.0 } } /// # `Modified` data extractor / response /// #[derive(Debug, Clone, Copy, Default)] pub struct Modified(pub E); impl Deref for Modified { type Target = E; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Modified { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Display for Modified { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } impl Modified { /// Consumes the `Modified` and returns the modified data within. /// /// This returns the `E` type which represents the data that has been /// modified. pub fn into_inner(self) -> E { self.0 } } impl IntoResponse for Modified { fn into_response(mut self) -> Response { self.get_modify().modify(); self.0.into_response() } } /// # `Validified` data extractor /// #[derive(Debug, Clone, Copy, Default)] pub struct Validified(pub E); impl Deref for Validified { type Target = E; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Validified { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Display for Validified { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } impl Validified { /// Consumes the `ValidifiedMut` and returns the modified and validated data within. /// /// This returns the `E` type which represents the data that has been /// successfully validated. pub fn into_inner(self) -> E { self.0 } } /// `ValidifyRejection` is returned when the `Validified` / `ValidifiedMut` / `Modified` extractor fails. /// pub type ValidifyRejection = ValidationRejection; impl From for ValidifyRejection { fn from(value: ValidationErrors) -> Self { Self::Valid(value) } } /// Trait for types that can supply a reference that can be modified. /// /// Extractor types `T` that implement this trait can be used with `Modified`. /// pub trait HasModify { /// Inner type that can be modified type Modify: Modify; /// Get the inner value fn get_modify(&mut self) -> &mut Self::Modify; } /// Extractor to extract payload for constructing data pub trait PayloadExtractor { /// Type of payload for constructing data type Payload; /// Get payload from the extractor fn get_payload(self) -> Self::Payload; } /// Trait for types that can supply a reference that can be modified and validated using `validify`. /// /// Extractor types `T` that implement this trait can be used with `Validified`. /// pub trait HasValidify: Sized { /// Inner type that can be modified and validated using `validify`. type Validify: Validify; /// type PayloadExtractor: PayloadExtractor::Payload>; /// fn from_validified(v: Self::Validify) -> Self; } #[async_trait] impl FromRequest for Validated where State: Send + Sync, Body: Send + Sync + 'static, Extractor: HasValidate + FromRequest, Extractor::Validate: validify::Validate, { type Rejection = ValidifyRejection<>::Rejection>; async fn from_request(req: Request, state: &State) -> Result { let inner = Extractor::from_request(req, state) .await .map_err(ValidifyRejection::Inner)?; inner.get_validate().validate()?; Ok(Validated(inner)) } } #[async_trait] impl FromRequestParts for Validated where State: Send + Sync, Extractor: HasValidate + FromRequestParts, Extractor::Validate: Validate, { type Rejection = ValidifyRejection<>::Rejection>; async fn from_request_parts(parts: &mut Parts, state: &State) -> Result { let inner = Extractor::from_request_parts(parts, state) .await .map_err(ValidifyRejection::Inner)?; inner.get_validate().validate()?; Ok(Validated(inner)) } } #[async_trait] impl FromRequest for Modified where State: Send + Sync, Body: Send + Sync + 'static, Extractor: HasModify + FromRequest, { type Rejection = >::Rejection; async fn from_request(req: Request, state: &State) -> Result { let mut inner = Extractor::from_request(req, state).await?; inner.get_modify().modify(); Ok(Modified(inner)) } } #[async_trait] impl FromRequestParts for Modified where State: Send + Sync, Extractor: HasModify + FromRequestParts, { type Rejection = >::Rejection; async fn from_request_parts(parts: &mut Parts, state: &State) -> Result { let mut inner = Extractor::from_request_parts(parts, state).await?; inner.get_modify().modify(); Ok(Modified(inner)) } } #[async_trait] impl FromRequest for Validified where State: Send + Sync, Body: Send + Sync + 'static, Extractor: HasValidify, Extractor::Validify: Validify, Extractor::PayloadExtractor: FromRequest, { type Rejection = ValidifyRejection<>::Rejection>; async fn from_request(req: Request, state: &State) -> Result { let payload = Extractor::PayloadExtractor::from_request(req, state) .await .map_err(ValidifyRejection::Inner)?; Ok(Validified(Extractor::from_validified( Extractor::Validify::validify(payload.get_payload())?, ))) } } #[async_trait] impl FromRequestParts for Validified where State: Send + Sync, Extractor: HasValidify, Extractor::Validify: Validify, Extractor::PayloadExtractor: FromRequestParts, { type Rejection = ValidifyRejection<>::Rejection>; async fn from_request_parts(parts: &mut Parts, state: &State) -> Result { let payload = Extractor::PayloadExtractor::from_request_parts(parts, state) .await .map_err(ValidifyRejection::Inner)?; Ok(Validified(Extractor::from_validified( Extractor::Validify::validify(payload.get_payload())?, ))) } }