From cffea88d0760ff70db69da238a3d6698d5d1d08f Mon Sep 17 00:00:00 2001 From: gengteng Date: Thu, 28 Sep 2023 14:51:13 +0800 Subject: [PATCH] remove response_builder from ValidationContext; rename ValidationContext to ValidationArguments --- src/lib.rs | 326 +++++++++-------------------------------------------- 1 file changed, 53 insertions(+), 273 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index faa8528..e6da0c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,260 +122,86 @@ impl ValidEx { } } -/// `ValidationContext` configures the response returned when validation fails. +/// `ValidationArguments` configures the response returned when validation fails. /// -/// By providing a ValidationContext to the Valid extractor, you can customize +/// By providing a ValidationArguments to the Valid extractor, you can customize /// the HTTP status code and response body returned on validation failure. /// -#[derive(Debug, Copy, Clone)] -pub struct ValidationContext { - /// Validation error response builder - response_builder: fn(ValidationErrors) -> Response, +#[derive(Debug, Copy, Clone, Default)] +pub struct ValidationArguments { arguments: Arguments, } -#[cfg(feature = "json")] -fn json_response_builder(ve: ValidationErrors) -> Response { +fn response_builder(ve: ValidationErrors) -> Response { + #[cfg(feature = "into_json")] { (VALIDATION_ERROR_STATUS, axum::Json(ve)).into_response() } -} - -fn string_response_builder(ve: ValidationErrors) -> Response { + #[cfg(not(feature = "into_json"))] { (VALIDATION_ERROR_STATUS, ve.to_string()).into_response() } } -impl Default for ValidationContext { - fn default() -> Self { - fn response_builder(ve: ValidationErrors) -> Response { - #[cfg(feature = "into_json")] - { - json_response_builder(ve) - } - #[cfg(not(feature = "into_json"))] - { - string_response_builder(ve) - } - } - - Self { - response_builder, - arguments: Arguments::default(), - } +impl ValidationArguments<()> { + /// Creates a new `ValidationArguments`. + pub fn with_arguments(self, arguments: Arguments) -> ValidationArguments { + ValidationArguments { arguments } } } -impl ValidationContext<()> { - /// Construct a `ValidationContext` with a custom response builder function - /// - /// # Examples - /// - /// ```no_run - /// use axum::{response::IntoResponse, Json}; - /// use axum::http::StatusCode; - /// use axum::response::Response; - /// use validator::ValidationErrors; - /// use axum_valid::ValidationContext; - /// - /// - /// fn custom_response(errors: ValidationErrors) -> Response { - /// // return response with custom status code and body - /// (StatusCode::IM_A_TEAPOT, Json(errors)).into_response() - /// } - /// - /// let context = ValidationContext::custom(custom_response); - /// ``` - pub fn custom(response_builder: fn(ValidationErrors) -> Response) -> Self { - Self { - response_builder, - arguments: (), - } - } - - /// Construct a ValidationContext that returns a string response - /// - /// This will return a response with the validation errors formatted as a string - /// The response status code will be `400 Bad Request` by default, or `422 Unprocessable Entity` if the `422` feature is enabled. - pub fn string() -> Self { - Self { - response_builder: string_response_builder, - arguments: (), - } - } - - /// Construct a ValidationContext that returns a JSON response - /// - /// This will return a response with the validation errors serialized as JSON. - /// The response status code will be `400 Bad Request` by default, or `422 Unprocessable Entity` if the `422` feature is enabled. - /// - /// Requires the `json` feature to be enabled. - #[cfg(feature = "json")] - pub fn json() -> Self { - Self { - response_builder: json_response_builder, - arguments: (), - } - } - - /// Creates a new `ValidationContext` with arguments. - pub fn with_arguments(self, arguments: Arguments) -> ValidationContext { - ValidationContext { - response_builder: self.response_builder, - arguments, - } +impl ValidationArguments { + /// Creates a `ValidationArguments` with arguments + pub fn new(arguments: Arguments) -> Self { + Self { arguments } } } -impl ValidationContext { - /// Construct a `ValidationContext` with a custom response builder function - /// - /// # Examples - /// - /// ```no_run - /// use axum::{response::IntoResponse, Json}; - /// use axum::http::StatusCode; - /// use axum::response::Response; - /// use validator::ValidationErrors; - /// use axum_valid::ValidationContext; - /// - /// struct MyArguments; - /// - /// fn custom_response(errors: ValidationErrors) -> Response { - /// // return response with custom status code and body - /// (StatusCode::NOT_FOUND, Json(errors)).into_response() - /// } - /// - /// let context = ValidationContext::custom_with_arguments(custom_response, MyArguments); - /// ``` - pub fn custom_with_arguments( - response_builder: fn(ValidationErrors) -> Response, - arguments: Arguments, - ) -> Self { - Self { - response_builder, - arguments, - } - } - - /// Construct a ValidationContext that returns a string response - /// - /// This will return a response with the validation errors formatted as a string - /// The response status code will be `400 Bad Request` by default, or `422 Unprocessable Entity` if the `422` feature is enabled. - pub fn string_with_arguments(arguments: Arguments) -> Self { - Self { - response_builder: string_response_builder, - arguments, - } - } - - /// Construct a ValidationContext that returns a JSON response - /// - /// This will return a response with the validation errors serialized as JSON. - /// The response status code will be `400 Bad Request` by default, or `422 Unprocessable Entity` if the `422` feature is enabled. - /// - /// Requires the `json` feature to be enabled. - #[cfg(feature = "json")] - pub fn json_with_arguments(arguments: Arguments) -> Self { - Self { - response_builder: json_response_builder, - arguments, - } - } - - /// Creates a new `ValidationContext` with empty arguments. - pub fn without_arguments(&self) -> ValidationContext<()> { - ValidationContext { - response_builder: self.response_builder, - arguments: (), - } - } -} - -impl FromRef<()> for ValidationContext<()> { +impl FromRef<()> for ValidationArguments<()> { fn from_ref(_: &()) -> Self { - ValidationContext::default() + ValidationArguments::default() } } -/// `ValidError` is the error type returned when the `Valid` extractor fails. -/// -/// It has two variants: -/// -/// - Valid: Contains validation errors (ValidationErrors) when validation fails. -/// - Inner: Contains the inner extractor error when the internal extractor fails. +/// `ValidRejection` is returned when the `Valid` extractor fails. /// #[derive(Debug)] -pub enum ValidError { +pub enum ValidRejection { /// Validation errors Valid(ValidationErrors), /// Inner extractor error Inner(E), } -impl Display for ValidError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - ValidError::Valid(errors) => write!(f, "{errors}"), - ValidError::Inner(error) => write!(f, "{error}"), - } - } -} - -impl Error for ValidError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - ValidError::Valid(ve) => Some(ve), - ValidError::Inner(e) => Some(e), - } - } -} - -impl From for ValidError { - fn from(value: ValidationErrors) -> Self { - Self::Valid(value) - } -} - -/// `ValidRejection` is returned when the `Valid` extractor fails. -/// -/// It contains the underlying `ValidError` and handles converting it -/// into a proper HTTP response. -pub struct ValidRejection { - error: ValidError, - response_builder: fn(ValidationErrors) -> Response, -} - impl Display for ValidRejection { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match &self.error { - ValidError::Valid(errors) => write!(f, "{errors}"), - ValidError::Inner(error) => write!(f, "{error}"), + match self { + ValidRejection::Valid(errors) => write!(f, "{errors}"), + ValidRejection::Inner(error) => write!(f, "{error}"), } } } -impl Debug for ValidRejection { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&self.error, f) - } -} - impl Error for ValidRejection { fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.error { - ValidError::Valid(ve) => Some(ve), - ValidError::Inner(e) => Some(e), + match self { + ValidRejection::Valid(ve) => Some(ve), + ValidRejection::Inner(e) => Some(e), } } } +impl From for ValidRejection { + fn from(value: ValidationErrors) -> Self { + Self::Valid(value) + } +} + impl IntoResponse for ValidRejection { fn into_response(self) -> Response { - match self.error { - ValidError::Valid(ve) => (self.response_builder)(ve), - ValidError::Inner(e) => e.into_response(), + match self { + ValidRejection::Valid(ve) => response_builder(ve), + ValidRejection::Inner(e) => e.into_response(), } } } @@ -406,25 +232,14 @@ where B: Send + Sync + 'static, E: HasValidate + FromRequest, E::Validate: Validate, - ValidationContext<()>: FromRef, { type Rejection = ValidRejection<>::Rejection>; async fn from_request(req: Request, state: &S) -> Result { - let context: ValidationContext<()> = FromRef::from_ref(state); let inner = E::from_request(req, state) .await - .map_err(|e| ValidRejection { - error: ValidError::Inner(e), - response_builder: context.response_builder, - })?; - inner - .get_validate() - .validate() - .map_err(|e| ValidRejection { - error: ValidError::Valid(e), - response_builder: context.response_builder, - })?; + .map_err(ValidRejection::Inner)?; + inner.get_validate().validate()?; Ok(Valid(inner)) } } @@ -435,25 +250,14 @@ where S: Send + Sync, E: HasValidate + FromRequestParts, E::Validate: Validate, - ValidationContext<()>: FromRef, { type Rejection = ValidRejection<>::Rejection>; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - let context: ValidationContext<()> = FromRef::from_ref(state); let inner = E::from_request_parts(parts, state) .await - .map_err(|e| ValidRejection { - error: ValidError::Inner(e), - response_builder: context.response_builder, - })?; - inner - .get_validate() - .validate() - .map_err(|e| ValidRejection { - error: ValidError::Valid(e), - response_builder: context.response_builder, - })?; + .map_err(ValidRejection::Inner)?; + inner.get_validate().validate()?; Ok(Valid(inner)) } } @@ -466,29 +270,17 @@ where Arguments: Send + Sync, E: for<'v> HasValidateArgs<'v> + FromRequest, for<'v> >::ValidateArgs: ValidateArgs<'v, Args = &'v Arguments>, - ValidationContext: FromRef, + ValidationArguments: FromRef, { type Rejection = ValidRejection<>::Rejection>; async fn from_request(req: Request, state: &S) -> Result { - let ValidationContext { - response_builder, - arguments, - }: ValidationContext = FromRef::from_ref(state); + let ValidationArguments { arguments }: ValidationArguments = + FromRef::from_ref(state); let inner = E::from_request(req, state) .await - .map_err(|e| ValidRejection { - error: ValidError::Inner(e), - response_builder, - })?; - - inner - .get_validate_args() - .validate_args(&arguments) - .map_err(|e| ValidRejection { - error: ValidError::Valid(e), - response_builder, - })?; + .map_err(ValidRejection::Inner)?; + inner.get_validate_args().validate_args(&arguments)?; Ok(ValidEx(inner, arguments)) } } @@ -500,36 +292,24 @@ where Arguments: Send + Sync, E: for<'v> HasValidateArgs<'v> + FromRequestParts, for<'v> >::ValidateArgs: ValidateArgs<'v, Args = &'v Arguments>, - ValidationContext: FromRef, + ValidationArguments: FromRef, { type Rejection = ValidRejection<>::Rejection>; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - let ValidationContext { - response_builder, - arguments, - }: ValidationContext = FromRef::from_ref(state); + let ValidationArguments { arguments }: ValidationArguments = + FromRef::from_ref(state); let inner = E::from_request_parts(parts, state) .await - .map_err(|e| ValidRejection { - error: ValidError::Inner(e), - response_builder, - })?; - inner - .get_validate_args() - .validate_args(&arguments) - .map_err(|e| ValidRejection { - error: ValidError::Valid(e), - response_builder, - })?; - + .map_err(ValidRejection::Inner)?; + inner.get_validate_args().validate_args(&arguments)?; Ok(ValidEx(inner, arguments)) } } #[cfg(test)] pub mod tests { - use crate::{Valid, ValidError}; + use crate::{Valid, ValidRejection}; use reqwest::{RequestBuilder, StatusCode}; use serde::Serialize; use std::error::Error; @@ -595,24 +375,24 @@ pub mod tests { // ValidRejection::Valid Display let mut ve = ValidationErrors::new(); ve.add(TEST, ValidationError::new(TEST)); - let vr = ValidError::::Valid(ve.clone()); + let vr = ValidRejection::::Valid(ve.clone()); assert_eq!(vr.to_string(), ve.to_string()); // ValidRejection::Inner Display let inner = String::from(TEST); - let vr = ValidError::::Inner(inner.clone()); + let vr = ValidRejection::::Inner(inner.clone()); assert_eq!(inner.to_string(), vr.to_string()); // ValidRejection::Valid Error let mut ve = ValidationErrors::new(); ve.add(TEST, ValidationError::new(TEST)); - let vr = ValidError::::Valid(ve.clone()); + let vr = ValidRejection::::Valid(ve.clone()); assert!( matches!(vr.source(), Some(source) if source.downcast_ref::().is_some()) ); // ValidRejection::Valid Error - let vr = ValidError::::Inner(io::Error::new(io::ErrorKind::Other, TEST)); + let vr = ValidRejection::::Inner(io::Error::new(io::ErrorKind::Other, TEST)); assert!( matches!(vr.source(), Some(source) if source.downcast_ref::().is_some()) );