diff --git a/Cargo.toml b/Cargo.toml index 7ee9138..7ee45ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,13 +48,17 @@ version = "0.8.0" default-features = false optional = true +[dependencies.serde] +version = "1.0.188" +optional = true + [dev-dependencies] anyhow = "1.0.72" axum = { version = "0.6.20", features = ["macros"] } tokio = { version = "1.29.1", features = ["full"] } hyper = { version = "0.14.27", features = ["full"] } reqwest = { version = "0.11.18", features = ["json", "multipart"] } -serde = { version = "1.0.181", features = ["derive"] } +serde = { version = "1.0.188", features = ["derive"] } validator = { version = "0.16.0", features = ["derive"] } serde_json = "1.0.104" serde_yaml = "0.9.25" @@ -75,7 +79,7 @@ typed_header = ["axum/headers"] typed_multipart = ["axum_typed_multipart"] msgpack = ["axum-msgpack"] yaml = ["axum-yaml"] -into_json = ["json"] +into_json = ["json", "serde"] 422 = [] extra = ["axum-extra"] extra_typed_path = ["axum-extra/typed-routing"] diff --git a/src/garde.rs b/src/garde.rs index 7bb3f1a..ed2a419 100644 --- a/src/garde.rs +++ b/src/garde.rs @@ -8,14 +8,12 @@ #[cfg(test)] pub mod test; -use crate::{HasValidate, VALIDATION_ERROR_STATUS}; +use crate::{HasValidate, ValidationRejection}; 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 garde::{Report, Validate}; use std::fmt::{Display, Formatter}; use std::ops::{Deref, DerefMut}; @@ -54,61 +52,13 @@ impl Garde { } } -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() - } -} - /// `GardeRejection` is returned when the `Garde` extractor fails. /// -/// This enumeration captures two types of errors that can occur when using `Garde`: errors related to the validation -/// logic itself (encapsulated in `Garde`), and errors that may arise within the inner extractor (represented by `Inner`). -/// -#[derive(Debug)] -pub enum GardeRejection { - /// `Report` variant captures errors related to the validation logic. It contains `garde::Report` - /// 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), -} +pub type GardeRejection = ValidationRejection; -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(), - } +impl From for GardeRejection { + fn from(value: Report) -> Self { + Self::Valid(value) } } @@ -158,6 +108,7 @@ where mod tests { use super::*; use garde::{Path, Report}; + use std::error::Error; use std::io; const GARDE: &str = "garde"; @@ -176,25 +127,25 @@ mod tests { #[test] fn display_error() { - // ValidRejection::Valid Display + // GardeRejection::Valid Display let mut report = Report::new(); report.append(Path::empty(), garde::Error::new(GARDE)); let s = report.to_string(); - let vr = GardeRejection::::Report(report); + let vr = GardeRejection::::Valid(report); assert_eq!(vr.to_string(), s); - // ValidRejection::Inner Display + // GardeRejection::Inner Display let inner = String::from(GARDE); let vr = GardeRejection::::Inner(inner.clone()); assert_eq!(inner.to_string(), vr.to_string()); - // ValidRejection::Valid Error + // GardeRejection::Valid Error let mut report = Report::new(); report.append(Path::empty(), garde::Error::new(GARDE)); - let vr = GardeRejection::::Report(report); + let vr = GardeRejection::::Valid(report); assert!(matches!(vr.source(), Some(source) if source.downcast_ref::().is_some())); - // ValidRejection::Valid Error + // GardeRejection::Valid Error let vr = GardeRejection::::Inner(io::Error::new(io::ErrorKind::Other, GARDE)); assert!( matches!(vr.source(), Some(source) if source.downcast_ref::().is_some()) diff --git a/src/lib.rs b/src/lib.rs index 22f75cd..984d8ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,9 @@ pub mod validator; pub mod yaml; use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; +use std::error::Error; +use std::fmt::Display; /// Http status code returned when there are validation errors. #[cfg(feature = "422")] @@ -49,6 +52,61 @@ pub use crate::validator::{Arguments, HasValidateArgs, Valid, ValidEx, ValidReje #[cfg(feature = "garde")] pub use crate::garde::{Garde, GardeRejection}; +/// `ValidationRejection` is returned when the validation extractor fails. +/// +/// This enumeration captures two types of errors that can occur when using `Valid`: errors related to the validation +/// extractor itself , and errors that may arise within the inner extractor (represented by `Inner`). +/// +#[derive(Debug)] +pub enum ValidationRejection { + /// `Valid` variant captures errors related to the validation logic. + Valid(V), + /// `Inner` variant represents potential errors that might occur within the inner extractor. + Inner(E), +} + +impl Display for ValidationRejection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ValidationRejection::Valid(errors) => write!(f, "{errors}"), + ValidationRejection::Inner(error) => write!(f, "{error}"), + } + } +} + +impl Error for ValidationRejection { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + ValidationRejection::Valid(ve) => Some(ve), + ValidationRejection::Inner(e) => Some(e), + } + } +} + +#[cfg(feature = "into_json")] +impl IntoResponse for ValidationRejection { + fn into_response(self) -> Response { + match self { + ValidationRejection::Valid(v) => { + (VALIDATION_ERROR_STATUS, axum::Json(v)).into_response() + } + ValidationRejection::Inner(e) => e.into_response(), + } + } +} + +#[cfg(not(feature = "into_json"))] +impl IntoResponse for ValidationRejection { + fn into_response(self) -> Response { + match self { + ValidationRejection::Valid(v) => { + (VALIDATION_ERROR_STATUS, v.to_string()).into_response() + } + ValidationRejection::Inner(e) => e.into_response(), + } + } +} + #[cfg(test)] mod tests { use reqwest::{RequestBuilder, StatusCode}; diff --git a/src/validator.rs b/src/validator.rs index dbafa0b..7974858 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -8,13 +8,11 @@ #[cfg(test)] pub mod test; -use crate::{HasValidate, VALIDATION_ERROR_STATUS}; +use crate::{HasValidate, ValidationRejection}; 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 std::error::Error; use std::fmt::Display; use std::ops::{Deref, DerefMut}; use validator::{Validate, ValidateArgs, ValidationErrors}; @@ -118,17 +116,6 @@ impl ValidEx { } } -fn response_builder(ve: ValidationErrors) -> 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 @@ -146,37 +133,9 @@ pub trait Arguments<'a> { fn get(&'a self) -> <>::T as ValidateArgs<'a>>::Args; } -/// `ValidRejection` is returned when the `Valid` extractor fails. +/// `ValidRejection` is returned when the `Valid` or `ValidEx` 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 ValidRejection { - /// `Valid` variant captures errors related to the validation logic. It contains `ValidationErrors` - /// which is a collection of validation failures for each field. - Valid(ValidationErrors), - /// `Inner` variant represents potential errors that might occur within the inner extractor. - Inner(E), -} - -impl Display for ValidRejection { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ValidRejection::Valid(errors) => write!(f, "{errors}"), - ValidRejection::Inner(error) => write!(f, "{error}"), - } - } -} - -impl Error for ValidRejection { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - ValidRejection::Valid(ve) => Some(ve), - ValidRejection::Inner(e) => Some(e), - } - } -} +pub type ValidRejection = ValidationRejection; impl From for ValidRejection { fn from(value: ValidationErrors) -> Self { @@ -184,15 +143,6 @@ impl From for ValidRejection { } } -impl IntoResponse for ValidRejection { - fn into_response(self) -> Response { - match self { - ValidRejection::Valid(ve) => response_builder(ve), - ValidRejection::Inner(e) => e.into_response(), - } - } -} - /// Trait for types that can supply a reference that can be validated using arguments. /// /// Extractor types `T` that implement this trait can be used with `ValidEx`. @@ -292,6 +242,7 @@ where #[cfg(test)] pub mod tests { use super::*; + use std::error::Error; use std::fmt::Formatter; use std::io; use validator::ValidationError;