impl ValidArgs

This commit is contained in:
gengteng
2023-09-26 18:06:27 +08:00
parent 1d080cef0c
commit f4a2d2c2de

View File

@@ -29,7 +29,7 @@ use axum::response::{IntoResponse, Response};
use std::error::Error; use std::error::Error;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use validator::{Validate, ValidationErrors}; use validator::{Validate, ValidateArgs, ValidationErrors};
/// Http status code returned when there are validation errors. /// Http status code returned when there are validation errors.
#[cfg(feature = "422")] #[cfg(feature = "422")]
@@ -73,6 +73,31 @@ impl<E> Valid<E> {
} }
} }
///
#[derive(Debug, Clone, Copy, Default)]
pub struct ValidArgs<E>(pub E);
impl<E> Deref for ValidArgs<E> {
type Target = E;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<E> DerefMut for ValidArgs<E> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<E> ValidArgs<E> {
/// Consume the `ValidArgs` extractor and returns the inner type.
pub fn into_inner(self) -> E {
self.0
}
}
/// `ValidationContext` configures the response returned when validation fails. /// `ValidationContext` configures the response returned when validation fails.
/// ///
/// By providing a ValidationContext to the Valid extractor, you can customize /// By providing a ValidationContext to the Valid extractor, you can customize
@@ -260,6 +285,14 @@ pub trait HasValidate {
fn get_validate(&self) -> &Self::Validate; fn get_validate(&self) -> &Self::Validate;
} }
///
pub trait HasValidateArgs<'v> {
/// Inner type that can be validated for correctness
type ValidateArgs: ValidateArgs<'v>;
/// Get the inner value
fn get_validate_args(&self) -> &Self::ValidateArgs;
}
#[async_trait] #[async_trait]
impl<S, B, E> FromRequest<S, B> for Valid<E> impl<S, B, E> FromRequest<S, B> for Valid<E>
where where
@@ -319,6 +352,69 @@ where
} }
} }
#[async_trait]
impl<'v, S, B, E> FromRequest<S, B> for ValidArgs<E>
where
S: Send + Sync,
B: Send + Sync + 'static,
E: HasValidateArgs<'v> + FromRequest<S, B>,
E::ValidateArgs: ValidateArgs<'v>,
<<E as HasValidateArgs<'v>>::ValidateArgs as ValidateArgs<'v>>::Args: FromRef<S>,
ValidationContext: FromRef<S>,
{
type Rejection = ValidRejection<<E as FromRequest<S, B>>::Rejection>;
async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
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,
})?;
let args = FromRef::from_ref(state);
inner
.get_validate_args()
.validate_args(args)
.map_err(|e| ValidRejection {
error: ValidError::Valid(e),
response_builder: context.response_builder,
})?;
Ok(ValidArgs(inner))
}
}
#[async_trait]
impl<'v, S, E> FromRequestParts<S> for ValidArgs<E>
where
S: Send + Sync,
E: HasValidateArgs<'v> + FromRequestParts<S>,
E::ValidateArgs: ValidateArgs<'v>,
<<E as HasValidateArgs<'v>>::ValidateArgs as ValidateArgs<'v>>::Args: FromRef<S>,
ValidationContext: FromRef<S>,
{
type Rejection = ValidRejection<<E as FromRequestParts<S>>::Rejection>;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
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,
})?;
let args = FromRef::from_ref(state);
inner
.get_validate_args()
.validate_args(args)
.map_err(|e| ValidRejection {
error: ValidError::Valid(e),
response_builder: context.response_builder,
})?;
Ok(ValidArgs(inner))
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use crate::{Valid, ValidError}; use crate::{Valid, ValidError};