//! # Garde support use crate::{HasValidate, VALIDATION_ERROR_STATUS}; use axum::async_trait; use axum::extract::{FromRef, FromRequest, FromRequestParts}; use axum::http::request::Parts; use axum::http::Request; use axum::response::{IntoResponse, Response}; use garde::Validate; use std::error::Error; use std::fmt::{Display, Formatter}; use std::ops::{Deref, DerefMut}; /// # `Garde` data extractor /// #[derive(Debug, Clone, Copy, Default)] pub struct Garde(pub E, pub A); impl Deref for Garde { type Target = E; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Garde { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl Display for Garde { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } impl Garde { /// Consumes the `ValidEx` 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 } /// Returns a reference to the validation arguments. /// /// This provides access to the `A` type which contains the arguments used /// to validate the data. These arguments were passed to the validation /// function. pub fn arguments<'a>(&'a self) -> <::T as Validate>::Context where A: GardeArgument, { self.1.get() } } fn response_builder(ve: garde::Report) -> Response { #[cfg(feature = "into_json")] { (VALIDATION_ERROR_STATUS, axum::Json(ve)).into_response() } #[cfg(not(feature = "into_json"))] { (VALIDATION_ERROR_STATUS, ve.to_string()).into_response() } } /// `Arguments` provides the validation arguments for the data type `T`. /// /// This trait has an associated type `T` which represents the data type to /// validate. `T` must implement the `ValidateArgs` trait which defines the /// validation logic. /// /// It's important to mention that types implementing `Arguments` should be a part of the router's state /// (either through implementing `FromRef` or by directly becoming the state) /// to enable automatic arguments retrieval during validation. /// pub trait GardeArgument { /// The data type to validate using this arguments type T: Validate; /// This method gets the arguments required by `ValidateArgs::validate_args` fn get(&self) -> <::T as Validate>::Context; } /// `ValidRejection` is returned when the `Valid` extractor fails. /// /// This enumeration captures two types of errors that can occur when using `Valid`: errors related to the validation /// logic itself (encapsulated in `Valid`), and errors that may arise within the inner extractor (represented by `Inner`). /// #[derive(Debug)] pub enum GardeRejection { /// `Valid` variant captures errors related to the validation logic. It contains `ValidationErrors` /// which is a collection of validation failures for each field. Report(garde::Report), /// `Inner` variant represents potential errors that might occur within the inner extractor. Inner(E), } impl Display for GardeRejection { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { GardeRejection::Report(errors) => write!(f, "{errors}"), GardeRejection::Inner(error) => write!(f, "{error}"), } } } impl Error for GardeRejection { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { GardeRejection::Report(ve) => Some(ve), GardeRejection::Inner(e) => Some(e), } } } impl From for GardeRejection { fn from(value: garde::Report) -> Self { Self::Report(value) } } impl IntoResponse for GardeRejection { fn into_response(self) -> Response { match self { GardeRejection::Report(ve) => response_builder(ve), GardeRejection::Inner(e) => e.into_response(), } } } #[async_trait] impl FromRequest for Garde where State: Send + Sync, Body: Send + Sync + 'static, Args: Send + Sync + FromRef + GardeArgument::Validate>, Extractor: HasValidate + FromRequest, ::Validate: garde::Validate, { type Rejection = GardeRejection<>::Rejection>; async fn from_request(req: Request, state: &State) -> Result { let arguments: Args = FromRef::from_ref(state); let inner = Extractor::from_request(req, state) .await .map_err(GardeRejection::Inner)?; inner.get_validate().validate(&arguments.get())?; Ok(Garde(inner, arguments)) } } #[async_trait] impl FromRequestParts for Garde where State: Send + Sync, Args: Send + Sync + FromRef + GardeArgument::Validate>, Extractor: HasValidate + FromRequestParts, ::Validate: garde::Validate, { type Rejection = GardeRejection<>::Rejection>; async fn from_request_parts(parts: &mut Parts, state: &State) -> Result { let arguments: Args = FromRef::from_ref(state); let inner = Extractor::from_request_parts(parts, state) .await .map_err(GardeRejection::Inner)?; inner.get_validate().validate(&arguments.get())?; Ok(Garde(inner, arguments)) } }