From 21ee4e7913e49281f439f554ad47d1f143034308 Mon Sep 17 00:00:00 2001 From: gengteng Date: Sat, 7 Oct 2023 11:46:25 +0800 Subject: [PATCH] fix compilation error --- Cargo.toml | 1 + src/garde.rs | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 +- 3 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/garde.rs diff --git a/Cargo.toml b/Cargo.toml index 1e0bb2e..d0d4764 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ features = ["all_types"] [dependencies] axum = { version = "0.6.20", default-features = false } +garde = "0.15.0" validator = "0.16.1" [dependencies.axum_typed_multipart] diff --git a/src/garde.rs b/src/garde.rs new file mode 100644 index 0000000..d636574 --- /dev/null +++ b/src/garde.rs @@ -0,0 +1,176 @@ +//! # 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)) + } +} diff --git a/src/lib.rs b/src/lib.rs index d7b21c8..8ca8cfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub mod extra; #[cfg(feature = "form")] pub mod form; +pub mod garde; #[cfg(feature = "json")] pub mod json; #[cfg(feature = "msgpack")] @@ -218,7 +219,7 @@ impl IntoResponse for ValidRejection { /// pub trait HasValidate { /// Inner type that can be validated for correctness - type Validate: Validate; + type Validate; /// Get the inner value fn get_validate(&self) -> &Self::Validate; }