update
This commit is contained in:
@@ -25,8 +25,8 @@ features = ["all_types"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.6.20", default-features = false }
|
axum = { version = "0.6.20", default-features = false }
|
||||||
garde = "0.15.0"
|
garde = { version = "0.15.0", optional = true }
|
||||||
validator = "0.16.1"
|
validator = { version = "0.16.1", optional = true}
|
||||||
|
|
||||||
[dependencies.axum_typed_multipart]
|
[dependencies.axum_typed_multipart]
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|||||||
@@ -236,8 +236,11 @@ pub mod query;
|
|||||||
#[cfg(feature = "extra_typed_path")]
|
#[cfg(feature = "extra_typed_path")]
|
||||||
pub mod typed_path;
|
pub mod typed_path;
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum_extra::extract::{Cached, WithRejection};
|
use axum_extra::extract::{Cached, WithRejection};
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Cached<T> {
|
impl<T> HasValidate for Cached<T> {
|
||||||
@@ -248,6 +251,7 @@ impl<T> HasValidate for Cached<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Cached<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Cached<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
@@ -262,6 +266,7 @@ impl<T, R> HasValidate for WithRejection<T, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for WithRejection<T, R> {
|
impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for WithRejection<T, R> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
@@ -272,6 +277,7 @@ impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for WithRejection<T, R> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{Rejection, ValidTest};
|
use crate::tests::{Rejection, ValidTest};
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use crate::Valid;
|
use crate::Valid;
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum_extra::extract::{Cached, WithRejection};
|
use axum_extra::extract::{Cached, WithRejection};
|
||||||
@@ -311,6 +317,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<T: ValidTest, R> ValidTest for WithRejection<Valid<T>, R> {
|
impl<T: ValidTest, R> ValidTest for WithRejection<Valid<T>, R> {
|
||||||
// just use `418 I'm a teapot` to test
|
// just use `418 I'm a teapot` to test
|
||||||
const ERROR_STATUS_CODE: StatusCode = StatusCode::IM_A_TEAPOT;
|
const ERROR_STATUS_CODE: StatusCode = StatusCode::IM_A_TEAPOT;
|
||||||
|
|||||||
@@ -38,8 +38,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum_extra::extract::Form;
|
use axum_extra::extract::Form;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Form<T> {
|
impl<T> HasValidate for Form<T> {
|
||||||
@@ -49,6 +52,7 @@ impl<T> HasValidate for Form<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Form<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Form<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
@@ -40,8 +40,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum_extra::protobuf::Protobuf;
|
use axum_extra::protobuf::Protobuf;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Protobuf<T> {
|
impl<T> HasValidate for Protobuf<T> {
|
||||||
@@ -51,6 +54,7 @@ impl<T> HasValidate for Protobuf<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Protobuf<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Protobuf<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
@@ -41,8 +41,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum_extra::extract::Query;
|
use axum_extra::extract::Query;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Query<T> {
|
impl<T> HasValidate for Query<T> {
|
||||||
@@ -52,6 +55,7 @@ impl<T> HasValidate for Query<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Query<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Query<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
@@ -49,19 +49,26 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::garde::Garde;
|
#[cfg(feature = "garde")]
|
||||||
|
use crate::Garde;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use crate::{Valid, ValidEx};
|
use crate::{Valid, ValidEx};
|
||||||
|
#[cfg(any(feature = "validator", feature = "garde"))]
|
||||||
use axum_extra::routing::TypedPath;
|
use axum_extra::routing::TypedPath;
|
||||||
|
#[cfg(any(feature = "validator", feature = "garde"))]
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<T: TypedPath + Display> TypedPath for Valid<T> {
|
impl<T: TypedPath + Display> TypedPath for Valid<T> {
|
||||||
const PATH: &'static str = T::PATH;
|
const PATH: &'static str = T::PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<T: TypedPath + Display, A> TypedPath for ValidEx<T, A> {
|
impl<T: TypedPath + Display, A> TypedPath for ValidEx<T, A> {
|
||||||
const PATH: &'static str = T::PATH;
|
const PATH: &'static str = T::PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
impl<T: TypedPath + Display> TypedPath for Garde<T> {
|
impl<T: TypedPath + Display> TypedPath for Garde<T> {
|
||||||
const PATH: &'static str = T::PATH;
|
const PATH: &'static str = T::PATH;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum::Form;
|
use axum::Form;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Form<T> {
|
impl<T> HasValidate for Form<T> {
|
||||||
@@ -49,6 +52,7 @@ impl<T> HasValidate for Form<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Form<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Form<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
18
src/garde.rs
18
src/garde.rs
@@ -145,3 +145,21 @@ where
|
|||||||
Ok(Garde(inner))
|
Ok(Garde(inner))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const GARDE: &str = "garde";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn garde_deref_deref_mut_into_inner() {
|
||||||
|
let mut inner = String::from(GARDE);
|
||||||
|
let mut v = Garde(inner.clone());
|
||||||
|
assert_eq!(&inner, v.deref());
|
||||||
|
inner.push_str(GARDE);
|
||||||
|
v.deref_mut().push_str(GARDE);
|
||||||
|
assert_eq!(&inner, v.deref());
|
||||||
|
assert_eq!(inner, v.into_inner());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
|
//!
|
||||||
//! use axum::routing::post;
|
//! use axum::routing::post;
|
||||||
//! use axum::Json;
|
//! use axum::Json;
|
||||||
//! use axum::Router;
|
//! use axum::Router;
|
||||||
@@ -38,8 +39,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Json<T> {
|
impl<T> HasValidate for Json<T> {
|
||||||
@@ -49,6 +53,7 @@ impl<T> HasValidate for Json<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Json<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Json<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
374
src/lib.rs
374
src/lib.rs
@@ -5,6 +5,7 @@
|
|||||||
pub mod extra;
|
pub mod extra;
|
||||||
#[cfg(feature = "form")]
|
#[cfg(feature = "form")]
|
||||||
pub mod form;
|
pub mod form;
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
pub mod garde;
|
pub mod garde;
|
||||||
#[cfg(feature = "json")]
|
#[cfg(feature = "json")]
|
||||||
pub mod json;
|
pub mod json;
|
||||||
@@ -14,23 +15,18 @@ pub mod path;
|
|||||||
#[cfg(feature = "query")]
|
#[cfg(feature = "query")]
|
||||||
pub mod query;
|
pub mod query;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[cfg(all(feature = "garde", feature = "validator"))]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
#[cfg(feature = "typed_header")]
|
#[cfg(feature = "typed_header")]
|
||||||
pub mod typed_header;
|
pub mod typed_header;
|
||||||
#[cfg(feature = "typed_multipart")]
|
#[cfg(feature = "typed_multipart")]
|
||||||
pub mod typed_multipart;
|
pub mod typed_multipart;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
mod validator;
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
pub mod yaml;
|
pub mod yaml;
|
||||||
|
|
||||||
use axum::async_trait;
|
use axum::http::StatusCode;
|
||||||
use axum::extract::{FromRef, FromRequest, FromRequestParts};
|
|
||||||
use axum::http::request::Parts;
|
|
||||||
use axum::http::{Request, StatusCode};
|
|
||||||
use axum::response::{IntoResponse, Response};
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
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")]
|
||||||
@@ -39,180 +35,6 @@ pub const VALIDATION_ERROR_STATUS: StatusCode = StatusCode::UNPROCESSABLE_ENTITY
|
|||||||
#[cfg(not(feature = "422"))]
|
#[cfg(not(feature = "422"))]
|
||||||
pub const VALIDATION_ERROR_STATUS: StatusCode = StatusCode::BAD_REQUEST;
|
pub const VALIDATION_ERROR_STATUS: StatusCode = StatusCode::BAD_REQUEST;
|
||||||
|
|
||||||
/// # `Valid` data extractor
|
|
||||||
///
|
|
||||||
/// This extractor can be used in combination with axum's extractors like
|
|
||||||
/// Json, Form, Query, Path, etc to validate their inner data automatically.
|
|
||||||
/// It can also work with custom extractors that implement the `HasValidate` trait.
|
|
||||||
///
|
|
||||||
/// See the docs for each integration module to find examples of using
|
|
||||||
/// `Valid` with that extractor.
|
|
||||||
///
|
|
||||||
/// For examples with custom extractors, check out the `tests/custom.rs` file.
|
|
||||||
///
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
pub struct Valid<E>(pub E);
|
|
||||||
|
|
||||||
impl<E> Deref for Valid<E> {
|
|
||||||
type Target = E;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> DerefMut for Valid<E> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Display, A> Display for ValidEx<T, A> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> Valid<E> {
|
|
||||||
/// Consume the `Valid` extractor and returns the inner type.
|
|
||||||
pub fn into_inner(self) -> E {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # `ValidEx` data extractor
|
|
||||||
///
|
|
||||||
/// `ValidEx` can be incorporated with extractors from various modules, similar to `Valid`.
|
|
||||||
/// Two differences exist between `ValidEx` and `Valid`:
|
|
||||||
///
|
|
||||||
/// - The inner data type in `ValidEx` implements `ValidateArgs` instead of `Validate`.
|
|
||||||
/// - `ValidEx` includes a second field that represents arguments used during validation of the first field.
|
|
||||||
///
|
|
||||||
/// The implementation of `ValidateArgs` is often automatically handled by validator's derive macros
|
|
||||||
/// (for more details, please refer to the validator's documentation).
|
|
||||||
///
|
|
||||||
/// Although current module documentation predominantly showcases `Valid` examples, the usage of `ValidEx` is analogous.
|
|
||||||
///
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
pub struct ValidEx<E, A>(pub E, pub A);
|
|
||||||
|
|
||||||
impl<E, A> Deref for ValidEx<E, A> {
|
|
||||||
type Target = E;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, A> DerefMut for ValidEx<E, A> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Display> Display for Valid<T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.0.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, A> ValidEx<E, A> {
|
|
||||||
/// 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) -> <<A as Arguments<'a>>::T as ValidateArgs<'a>>::Args
|
|
||||||
where
|
|
||||||
A: Arguments<'a>,
|
|
||||||
{
|
|
||||||
self.1.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
/// 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<StateType>` or by directly becoming the state)
|
|
||||||
/// to enable automatic arguments retrieval during validation.
|
|
||||||
///
|
|
||||||
pub trait Arguments<'a> {
|
|
||||||
/// The data type to validate using this arguments
|
|
||||||
type T: ValidateArgs<'a>;
|
|
||||||
/// This method gets the arguments required by `ValidateArgs::validate_args`
|
|
||||||
fn get(&'a self) -> <<Self as Arguments<'a>>::T as ValidateArgs<'a>>::Args;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `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 ValidRejection<E> {
|
|
||||||
/// `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<E: Display> Display for ValidRejection<E> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ValidRejection::Valid(errors) => write!(f, "{errors}"),
|
|
||||||
ValidRejection::Inner(error) => write!(f, "{error}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Error + 'static> Error for ValidRejection<E> {
|
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
|
||||||
match self {
|
|
||||||
ValidRejection::Valid(ve) => Some(ve),
|
|
||||||
ValidRejection::Inner(e) => Some(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> From<ValidationErrors> for ValidRejection<E> {
|
|
||||||
fn from(value: ValidationErrors) -> Self {
|
|
||||||
Self::Valid(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: IntoResponse> IntoResponse for ValidRejection<E> {
|
|
||||||
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.
|
/// Trait for types that can supply a reference that can be validated.
|
||||||
///
|
///
|
||||||
/// Extractor types `T` that implement this trait can be used with `Valid` or `Garde`.
|
/// Extractor types `T` that implement this trait can be used with `Valid` or `Garde`.
|
||||||
@@ -224,111 +46,16 @@ pub trait HasValidate {
|
|||||||
fn get_validate(&self) -> &Self::Validate;
|
fn get_validate(&self) -> &Self::Validate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for types that can supply a reference that can be validated using arguments.
|
#[cfg(feature = "validator")]
|
||||||
///
|
pub use crate::validator::{Arguments, HasValidateArgs, Valid, ValidEx, ValidRejection};
|
||||||
/// Extractor types `T` that implement this trait can be used with `ValidEx`.
|
|
||||||
///
|
|
||||||
pub trait HasValidateArgs<'v> {
|
|
||||||
/// Inner type that can be validated using arguments
|
|
||||||
type ValidateArgs: ValidateArgs<'v>;
|
|
||||||
/// Get the inner value
|
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg(feature = "garde")]
|
||||||
impl<State, Body, Extractor> FromRequest<State, Body> for Valid<Extractor>
|
pub use crate::garde::{Garde, GardeRejection};
|
||||||
where
|
|
||||||
State: Send + Sync,
|
|
||||||
Body: Send + Sync + 'static,
|
|
||||||
Extractor: HasValidate + FromRequest<State, Body>,
|
|
||||||
Extractor::Validate: Validate,
|
|
||||||
{
|
|
||||||
type Rejection = ValidRejection<<Extractor as FromRequest<State, Body>>::Rejection>;
|
|
||||||
|
|
||||||
async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
|
|
||||||
let inner = Extractor::from_request(req, state)
|
|
||||||
.await
|
|
||||||
.map_err(ValidRejection::Inner)?;
|
|
||||||
inner.get_validate().validate()?;
|
|
||||||
Ok(Valid(inner))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<State, Extractor> FromRequestParts<State> for Valid<Extractor>
|
|
||||||
where
|
|
||||||
State: Send + Sync,
|
|
||||||
Extractor: HasValidate + FromRequestParts<State>,
|
|
||||||
Extractor::Validate: Validate,
|
|
||||||
{
|
|
||||||
type Rejection = ValidRejection<<Extractor as FromRequestParts<State>>::Rejection>;
|
|
||||||
|
|
||||||
async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
|
|
||||||
let inner = Extractor::from_request_parts(parts, state)
|
|
||||||
.await
|
|
||||||
.map_err(ValidRejection::Inner)?;
|
|
||||||
inner.get_validate().validate()?;
|
|
||||||
Ok(Valid(inner))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<State, Body, Extractor, Args> FromRequest<State, Body> for ValidEx<Extractor, Args>
|
|
||||||
where
|
|
||||||
State: Send + Sync,
|
|
||||||
Body: Send + Sync + 'static,
|
|
||||||
Args: Send
|
|
||||||
+ Sync
|
|
||||||
+ FromRef<State>
|
|
||||||
+ for<'a> Arguments<'a, T = <Extractor as HasValidateArgs<'a>>::ValidateArgs>,
|
|
||||||
Extractor: for<'v> HasValidateArgs<'v> + FromRequest<State, Body>,
|
|
||||||
for<'v> <Extractor as HasValidateArgs<'v>>::ValidateArgs: ValidateArgs<'v>,
|
|
||||||
{
|
|
||||||
type Rejection = ValidRejection<<Extractor as FromRequest<State, Body>>::Rejection>;
|
|
||||||
|
|
||||||
async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
|
|
||||||
let arguments: Args = FromRef::from_ref(state);
|
|
||||||
let inner = Extractor::from_request(req, state)
|
|
||||||
.await
|
|
||||||
.map_err(ValidRejection::Inner)?;
|
|
||||||
|
|
||||||
inner.get_validate_args().validate_args(arguments.get())?;
|
|
||||||
Ok(ValidEx(inner, arguments))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<State, Extractor, Args> FromRequestParts<State> for ValidEx<Extractor, Args>
|
|
||||||
where
|
|
||||||
State: Send + Sync,
|
|
||||||
Args: Send
|
|
||||||
+ Sync
|
|
||||||
+ FromRef<State>
|
|
||||||
+ for<'a> Arguments<'a, T = <Extractor as HasValidateArgs<'a>>::ValidateArgs>,
|
|
||||||
Extractor: for<'v> HasValidateArgs<'v> + FromRequestParts<State>,
|
|
||||||
for<'v> <Extractor as HasValidateArgs<'v>>::ValidateArgs: ValidateArgs<'v>,
|
|
||||||
{
|
|
||||||
type Rejection = ValidRejection<<Extractor as FromRequestParts<State>>::Rejection>;
|
|
||||||
|
|
||||||
async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
|
|
||||||
let arguments: Args = FromRef::from_ref(state);
|
|
||||||
let inner = Extractor::from_request_parts(parts, state)
|
|
||||||
.await
|
|
||||||
.map_err(ValidRejection::Inner)?;
|
|
||||||
inner.get_validate_args().validate_args(arguments.get())?;
|
|
||||||
Ok(ValidEx(inner, arguments))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
mod tests {
|
||||||
use crate::{Arguments, Valid, ValidEx, ValidRejection};
|
|
||||||
use reqwest::{RequestBuilder, StatusCode};
|
use reqwest::{RequestBuilder, StatusCode};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::error::Error;
|
|
||||||
use std::io;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use validator::{Validate, ValidateArgs, ValidationError, ValidationErrors};
|
|
||||||
|
|
||||||
/// # Valid test parameter
|
/// # Valid test parameter
|
||||||
pub trait ValidTestParameter: Serialize + 'static {
|
pub trait ValidTestParameter: Serialize + 'static {
|
||||||
@@ -369,85 +96,4 @@ pub mod tests {
|
|||||||
pub trait Rejection {
|
pub trait Rejection {
|
||||||
const STATUS_CODE: StatusCode;
|
const STATUS_CODE: StatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TEST: &str = "test";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn valid_deref_deref_mut_into_inner() {
|
|
||||||
let mut inner = String::from(TEST);
|
|
||||||
let mut v = Valid(inner.clone());
|
|
||||||
assert_eq!(&inner, v.deref());
|
|
||||||
inner.push_str(TEST);
|
|
||||||
v.deref_mut().push_str(TEST);
|
|
||||||
assert_eq!(&inner, v.deref());
|
|
||||||
assert_eq!(inner, v.into_inner());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn valid_ex_deref_deref_mut_into_inner_arguments() {
|
|
||||||
let mut inner = String::from(TEST);
|
|
||||||
let mut v = ValidEx(inner.clone(), ());
|
|
||||||
assert_eq!(&inner, v.deref());
|
|
||||||
inner.push_str(TEST);
|
|
||||||
v.deref_mut().push_str(TEST);
|
|
||||||
assert_eq!(&inner, v.deref());
|
|
||||||
assert_eq!(inner, v.into_inner());
|
|
||||||
|
|
||||||
fn validate(_v: i32, _args: i32) -> Result<(), ValidationError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Validate)]
|
|
||||||
struct Data {
|
|
||||||
#[validate(custom(function = "validate", arg = "i32"))]
|
|
||||||
v: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DataVA {
|
|
||||||
a: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Arguments<'a> for DataVA {
|
|
||||||
type T = Data;
|
|
||||||
|
|
||||||
fn get(&'a self) -> <<Self as Arguments<'a>>::T as ValidateArgs<'a>>::Args {
|
|
||||||
self.a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = Data { v: 12 };
|
|
||||||
let args = DataVA { a: 123 };
|
|
||||||
let ve = ValidEx(data, args);
|
|
||||||
assert_eq!(ve.v, 12);
|
|
||||||
let a = ve.arguments();
|
|
||||||
assert_eq!(a, 123);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn display_error() {
|
|
||||||
// ValidRejection::Valid Display
|
|
||||||
let mut ve = ValidationErrors::new();
|
|
||||||
ve.add(TEST, ValidationError::new(TEST));
|
|
||||||
let vr = ValidRejection::<String>::Valid(ve.clone());
|
|
||||||
assert_eq!(vr.to_string(), ve.to_string());
|
|
||||||
|
|
||||||
// ValidRejection::Inner Display
|
|
||||||
let inner = String::from(TEST);
|
|
||||||
let vr = ValidRejection::<String>::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 = ValidRejection::<io::Error>::Valid(ve.clone());
|
|
||||||
assert!(
|
|
||||||
matches!(vr.source(), Some(source) if source.downcast_ref::<ValidationErrors>().is_some())
|
|
||||||
);
|
|
||||||
|
|
||||||
// ValidRejection::Valid Error
|
|
||||||
let vr = ValidRejection::<io::Error>::Inner(io::Error::new(io::ErrorKind::Other, TEST));
|
|
||||||
assert!(
|
|
||||||
matches!(vr.source(), Some(source) if source.downcast_ref::<io::Error>().is_some())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
|
//!
|
||||||
//! use axum::routing::post;
|
//! use axum::routing::post;
|
||||||
//! use axum::Router;
|
//! use axum::Router;
|
||||||
//! use axum_msgpack::{MsgPack, MsgPackRaw};
|
//! use axum_msgpack::{MsgPack, MsgPackRaw};
|
||||||
@@ -47,8 +48,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum_msgpack::{MsgPack, MsgPackRaw};
|
use axum_msgpack::{MsgPack, MsgPackRaw};
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for MsgPack<T> {
|
impl<T> HasValidate for MsgPack<T> {
|
||||||
@@ -58,6 +62,7 @@ impl<T> HasValidate for MsgPack<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPack<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPack<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
@@ -72,6 +77,7 @@ impl<T> HasValidate for MsgPackRaw<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPackRaw<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPackRaw<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
|
//!
|
||||||
//! use axum::extract::Path;
|
//! use axum::extract::Path;
|
||||||
//! use axum::routing::post;
|
//! use axum::routing::post;
|
||||||
//! use axum::Router;
|
//! use axum::Router;
|
||||||
@@ -37,8 +38,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Path<T> {
|
impl<T> HasValidate for Path<T> {
|
||||||
@@ -48,6 +52,7 @@ impl<T> HasValidate for Path<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Path<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Path<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
|
//!
|
||||||
//! use axum::extract::Query;
|
//! use axum::extract::Query;
|
||||||
//! use axum::routing::post;
|
//! use axum::routing::post;
|
||||||
//! use axum::Router;
|
//! use axum::Router;
|
||||||
@@ -41,8 +42,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum::extract::Query;
|
use axum::extract::Query;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Query<T> {
|
impl<T> HasValidate for Query<T> {
|
||||||
@@ -52,6 +56,7 @@ impl<T> HasValidate for Query<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Query<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Query<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
16
src/test.rs
16
src/test.rs
@@ -1,5 +1,5 @@
|
|||||||
use crate::garde::Garde;
|
|
||||||
use crate::tests::{ValidTest, ValidTestParameter};
|
use crate::tests::{ValidTest, ValidTestParameter};
|
||||||
|
use crate::Garde;
|
||||||
use crate::{Arguments, HasValidate, HasValidateArgs, Valid, ValidEx, VALIDATION_ERROR_STATUS};
|
use crate::{Arguments, HasValidate, HasValidateArgs, Valid, ValidEx, VALIDATION_ERROR_STATUS};
|
||||||
use axum::extract::{FromRef, Path, Query};
|
use axum::extract::{FromRef, Path, Query};
|
||||||
use axum::routing::{get, post};
|
use axum::routing::{get, post};
|
||||||
@@ -164,9 +164,12 @@ async fn test_main() -> anyhow::Result<()> {
|
|||||||
.route(route::JSON, post(extract_json))
|
.route(route::JSON, post(extract_json))
|
||||||
.route(route::PATH_EX, get(extract_path_ex))
|
.route(route::PATH_EX, get(extract_path_ex))
|
||||||
.route(route::QUERY_EX, get(extract_query_ex))
|
.route(route::QUERY_EX, get(extract_query_ex))
|
||||||
.route(route::QUERY_GARDE, get(extract_query_garde))
|
|
||||||
.route(route::FORM_EX, post(extract_form_ex))
|
.route(route::FORM_EX, post(extract_form_ex))
|
||||||
.route(route::JSON_EX, post(extract_json_ex))
|
.route(route::JSON_EX, post(extract_json_ex));
|
||||||
|
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
|
let router = router
|
||||||
|
.route(route::QUERY_GARDE, get(extract_query_garde))
|
||||||
.route(route::JSON_GARDE, post(extract_json_garde));
|
.route(route::JSON_GARDE, post(extract_json_garde));
|
||||||
|
|
||||||
#[cfg(feature = "typed_header")]
|
#[cfg(feature = "typed_header")]
|
||||||
@@ -367,6 +370,7 @@ async fn test_main() -> anyhow::Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Garde
|
// Garde
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
test_executor
|
test_executor
|
||||||
.execute::<Query<Parameters>>(Method::GET, route::QUERY_GARDE)
|
.execute::<Query<Parameters>>(Method::GET, route::QUERY_GARDE)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -392,6 +396,7 @@ async fn test_main() -> anyhow::Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Garde
|
// Garde
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
test_executor
|
test_executor
|
||||||
.execute::<Json<Parameters>>(Method::POST, route::JSON_GARDE)
|
.execute::<Json<Parameters>>(Method::POST, route::JSON_GARDE)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -698,11 +703,13 @@ mod route {
|
|||||||
pub const PATH_EX: &str = "/path_ex/:v0/:v1";
|
pub const PATH_EX: &str = "/path_ex/:v0/:v1";
|
||||||
pub const QUERY: &str = "/query";
|
pub const QUERY: &str = "/query";
|
||||||
pub const QUERY_EX: &str = "/query_ex";
|
pub const QUERY_EX: &str = "/query_ex";
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
pub const QUERY_GARDE: &str = "/query_garde";
|
pub const QUERY_GARDE: &str = "/query_garde";
|
||||||
pub const FORM: &str = "/form";
|
pub const FORM: &str = "/form";
|
||||||
pub const FORM_EX: &str = "/form_ex";
|
pub const FORM_EX: &str = "/form_ex";
|
||||||
pub const JSON: &str = "/json";
|
pub const JSON: &str = "/json";
|
||||||
pub const JSON_EX: &str = "/json_ex";
|
pub const JSON_EX: &str = "/json_ex";
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
pub const JSON_GARDE: &str = "/json_garde";
|
pub const JSON_GARDE: &str = "/json_garde";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,6 +733,7 @@ async fn extract_query_ex(
|
|||||||
validate_again_ex(parameters, args.get())
|
validate_again_ex(parameters, args.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
async fn extract_query_garde(
|
async fn extract_query_garde(
|
||||||
Garde(Query(parameters)): Garde<Query<ParametersGarde>>,
|
Garde(Query(parameters)): Garde<Query<ParametersGarde>>,
|
||||||
) -> StatusCode {
|
) -> StatusCode {
|
||||||
@@ -752,6 +760,7 @@ async fn extract_json_ex(
|
|||||||
validate_again_ex(parameters, args.get())
|
validate_again_ex(parameters, args.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
async fn extract_json_garde(Garde(Json(parameters)): Garde<Json<ParametersGarde>>) -> StatusCode {
|
async fn extract_json_garde(Garde(Json(parameters)): Garde<Json<ParametersGarde>>) -> StatusCode {
|
||||||
validate_again_garde(parameters, ())
|
validate_again_garde(parameters, ())
|
||||||
}
|
}
|
||||||
@@ -781,6 +790,7 @@ fn validate_again_ex<'v, V: ValidateArgs<'v>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "garde")]
|
||||||
fn validate_again_garde<V>(validate: V, context: V::Context) -> StatusCode
|
fn validate_again_garde<V>(validate: V, context: V::Context) -> StatusCode
|
||||||
where
|
where
|
||||||
V: garde::Validate,
|
V: garde::Validate,
|
||||||
|
|||||||
@@ -61,8 +61,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum::TypedHeader;
|
use axum::TypedHeader;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for TypedHeader<T> {
|
impl<T> HasValidate for TypedHeader<T> {
|
||||||
@@ -72,6 +75,7 @@ impl<T> HasValidate for TypedHeader<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for TypedHeader<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for TypedHeader<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
@@ -50,8 +50,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum_typed_multipart::{BaseMultipart, TypedMultipart};
|
use axum_typed_multipart::{BaseMultipart, TypedMultipart};
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T, R> HasValidate for BaseMultipart<T, R> {
|
impl<T, R> HasValidate for BaseMultipart<T, R> {
|
||||||
@@ -61,6 +64,7 @@ impl<T, R> HasValidate for BaseMultipart<T, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for BaseMultipart<T, R> {
|
impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for BaseMultipart<T, R> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
@@ -75,6 +79,7 @@ impl<T> HasValidate for TypedMultipart<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for TypedMultipart<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for TypedMultipart<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
367
src/validator.rs
Normal file
367
src/validator.rs
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
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 std::error::Error;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use validator::{Validate, ValidateArgs, ValidationErrors};
|
||||||
|
|
||||||
|
/// # `Valid` data extractor
|
||||||
|
///
|
||||||
|
/// This extractor can be used in combination with axum's extractors like
|
||||||
|
/// Json, Form, Query, Path, etc to validate their inner data automatically.
|
||||||
|
/// It can also work with custom extractors that implement the `HasValidate` trait.
|
||||||
|
///
|
||||||
|
/// See the docs for each integration module to find examples of using
|
||||||
|
/// `Valid` with that extractor.
|
||||||
|
///
|
||||||
|
/// For examples with custom extractors, check out the `tests/custom.rs` file.
|
||||||
|
///
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct Valid<E>(pub E);
|
||||||
|
|
||||||
|
impl<E> Deref for Valid<E> {
|
||||||
|
type Target = E;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> DerefMut for Valid<E> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display, A> Display for ValidEx<T, A> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> Valid<E> {
|
||||||
|
/// Consume the `Valid` extractor and returns the inner type.
|
||||||
|
pub fn into_inner(self) -> E {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # `ValidEx` data extractor
|
||||||
|
///
|
||||||
|
/// `ValidEx` can be incorporated with extractors from various modules, similar to `Valid`.
|
||||||
|
/// Two differences exist between `ValidEx` and `Valid`:
|
||||||
|
///
|
||||||
|
/// - The inner data type in `ValidEx` implements `ValidateArgs` instead of `Validate`.
|
||||||
|
/// - `ValidEx` includes a second field that represents arguments used during validation of the first field.
|
||||||
|
///
|
||||||
|
/// The implementation of `ValidateArgs` is often automatically handled by validator's derive macros
|
||||||
|
/// (for more details, please refer to the validator's documentation).
|
||||||
|
///
|
||||||
|
/// Although current module documentation predominantly showcases `Valid` examples, the usage of `ValidEx` is analogous.
|
||||||
|
///
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct ValidEx<E, A>(pub E, pub A);
|
||||||
|
|
||||||
|
impl<E, A> Deref for ValidEx<E, A> {
|
||||||
|
type Target = E;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, A> DerefMut for ValidEx<E, A> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Display for Valid<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, A> ValidEx<E, A> {
|
||||||
|
/// 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) -> <<A as Arguments<'a>>::T as ValidateArgs<'a>>::Args
|
||||||
|
where
|
||||||
|
A: Arguments<'a>,
|
||||||
|
{
|
||||||
|
self.1.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
/// 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<StateType>` or by directly becoming the state)
|
||||||
|
/// to enable automatic arguments retrieval during validation.
|
||||||
|
///
|
||||||
|
pub trait Arguments<'a> {
|
||||||
|
/// The data type to validate using this arguments
|
||||||
|
type T: ValidateArgs<'a>;
|
||||||
|
/// This method gets the arguments required by `ValidateArgs::validate_args`
|
||||||
|
fn get(&'a self) -> <<Self as Arguments<'a>>::T as ValidateArgs<'a>>::Args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `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 ValidRejection<E> {
|
||||||
|
/// `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<E: Display> Display for ValidRejection<E> {
|
||||||
|
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<E: Error + 'static> Error for ValidRejection<E> {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
ValidRejection::Valid(ve) => Some(ve),
|
||||||
|
ValidRejection::Inner(e) => Some(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> From<ValidationErrors> for ValidRejection<E> {
|
||||||
|
fn from(value: ValidationErrors) -> Self {
|
||||||
|
Self::Valid(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: IntoResponse> IntoResponse for ValidRejection<E> {
|
||||||
|
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`.
|
||||||
|
///
|
||||||
|
pub trait HasValidateArgs<'v> {
|
||||||
|
/// Inner type that can be validated using arguments
|
||||||
|
type ValidateArgs: ValidateArgs<'v>;
|
||||||
|
/// Get the inner value
|
||||||
|
fn get_validate_args(&self) -> &Self::ValidateArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<State, Body, Extractor> FromRequest<State, Body> for Valid<Extractor>
|
||||||
|
where
|
||||||
|
State: Send + Sync,
|
||||||
|
Body: Send + Sync + 'static,
|
||||||
|
Extractor: HasValidate + FromRequest<State, Body>,
|
||||||
|
Extractor::Validate: Validate,
|
||||||
|
{
|
||||||
|
type Rejection = ValidRejection<<Extractor as FromRequest<State, Body>>::Rejection>;
|
||||||
|
|
||||||
|
async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
|
||||||
|
let inner = Extractor::from_request(req, state)
|
||||||
|
.await
|
||||||
|
.map_err(ValidRejection::Inner)?;
|
||||||
|
inner.get_validate().validate()?;
|
||||||
|
Ok(Valid(inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<State, Extractor> FromRequestParts<State> for Valid<Extractor>
|
||||||
|
where
|
||||||
|
State: Send + Sync,
|
||||||
|
Extractor: HasValidate + FromRequestParts<State>,
|
||||||
|
Extractor::Validate: Validate,
|
||||||
|
{
|
||||||
|
type Rejection = ValidRejection<<Extractor as FromRequestParts<State>>::Rejection>;
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
|
||||||
|
let inner = Extractor::from_request_parts(parts, state)
|
||||||
|
.await
|
||||||
|
.map_err(ValidRejection::Inner)?;
|
||||||
|
inner.get_validate().validate()?;
|
||||||
|
Ok(Valid(inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<State, Body, Extractor, Args> FromRequest<State, Body> for ValidEx<Extractor, Args>
|
||||||
|
where
|
||||||
|
State: Send + Sync,
|
||||||
|
Body: Send + Sync + 'static,
|
||||||
|
Args: Send
|
||||||
|
+ Sync
|
||||||
|
+ FromRef<State>
|
||||||
|
+ for<'a> Arguments<'a, T = <Extractor as HasValidateArgs<'a>>::ValidateArgs>,
|
||||||
|
Extractor: for<'v> HasValidateArgs<'v> + FromRequest<State, Body>,
|
||||||
|
for<'v> <Extractor as HasValidateArgs<'v>>::ValidateArgs: ValidateArgs<'v>,
|
||||||
|
{
|
||||||
|
type Rejection = ValidRejection<<Extractor as FromRequest<State, Body>>::Rejection>;
|
||||||
|
|
||||||
|
async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
|
||||||
|
let arguments: Args = FromRef::from_ref(state);
|
||||||
|
let inner = Extractor::from_request(req, state)
|
||||||
|
.await
|
||||||
|
.map_err(ValidRejection::Inner)?;
|
||||||
|
|
||||||
|
inner.get_validate_args().validate_args(arguments.get())?;
|
||||||
|
Ok(ValidEx(inner, arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<State, Extractor, Args> FromRequestParts<State> for ValidEx<Extractor, Args>
|
||||||
|
where
|
||||||
|
State: Send + Sync,
|
||||||
|
Args: Send
|
||||||
|
+ Sync
|
||||||
|
+ FromRef<State>
|
||||||
|
+ for<'a> Arguments<'a, T = <Extractor as HasValidateArgs<'a>>::ValidateArgs>,
|
||||||
|
Extractor: for<'v> HasValidateArgs<'v> + FromRequestParts<State>,
|
||||||
|
for<'v> <Extractor as HasValidateArgs<'v>>::ValidateArgs: ValidateArgs<'v>,
|
||||||
|
{
|
||||||
|
type Rejection = ValidRejection<<Extractor as FromRequestParts<State>>::Rejection>;
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
|
||||||
|
let arguments: Args = FromRef::from_ref(state);
|
||||||
|
let inner = Extractor::from_request_parts(parts, state)
|
||||||
|
.await
|
||||||
|
.map_err(ValidRejection::Inner)?;
|
||||||
|
inner.get_validate_args().validate_args(arguments.get())?;
|
||||||
|
Ok(ValidEx(inner, arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io;
|
||||||
|
use validator::ValidationError;
|
||||||
|
const TEST: &str = "test";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_deref_deref_mut_into_inner() {
|
||||||
|
let mut inner = String::from(TEST);
|
||||||
|
let mut v = Valid(inner.clone());
|
||||||
|
assert_eq!(&inner, v.deref());
|
||||||
|
inner.push_str(TEST);
|
||||||
|
v.deref_mut().push_str(TEST);
|
||||||
|
assert_eq!(&inner, v.deref());
|
||||||
|
assert_eq!(inner, v.into_inner());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_ex_deref_deref_mut_into_inner_arguments() {
|
||||||
|
let mut inner = String::from(TEST);
|
||||||
|
let mut v = ValidEx(inner.clone(), ());
|
||||||
|
assert_eq!(&inner, v.deref());
|
||||||
|
inner.push_str(TEST);
|
||||||
|
v.deref_mut().push_str(TEST);
|
||||||
|
assert_eq!(&inner, v.deref());
|
||||||
|
assert_eq!(inner, v.into_inner());
|
||||||
|
|
||||||
|
fn validate(_v: i32, _args: i32) -> Result<(), ValidationError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Validate)]
|
||||||
|
struct Data {
|
||||||
|
#[validate(custom(function = "validate", arg = "i32"))]
|
||||||
|
v: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DataVA {
|
||||||
|
a: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Arguments<'a> for DataVA {
|
||||||
|
type T = Data;
|
||||||
|
|
||||||
|
fn get(&'a self) -> <<Self as Arguments<'a>>::T as ValidateArgs<'a>>::Args {
|
||||||
|
self.a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = Data { v: 12 };
|
||||||
|
let args = DataVA { a: 123 };
|
||||||
|
let ve = ValidEx(data, args);
|
||||||
|
assert_eq!(ve.v, 12);
|
||||||
|
let a = ve.arguments();
|
||||||
|
assert_eq!(a, 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_error() {
|
||||||
|
// ValidRejection::Valid Display
|
||||||
|
let mut ve = ValidationErrors::new();
|
||||||
|
ve.add(TEST, ValidationError::new(TEST));
|
||||||
|
let vr = ValidRejection::<String>::Valid(ve.clone());
|
||||||
|
assert_eq!(vr.to_string(), ve.to_string());
|
||||||
|
|
||||||
|
// ValidRejection::Inner Display
|
||||||
|
let inner = String::from(TEST);
|
||||||
|
let vr = ValidRejection::<String>::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 = ValidRejection::<io::Error>::Valid(ve.clone());
|
||||||
|
assert!(
|
||||||
|
matches!(vr.source(), Some(source) if source.downcast_ref::<ValidationErrors>().is_some())
|
||||||
|
);
|
||||||
|
|
||||||
|
// ValidRejection::Valid Error
|
||||||
|
let vr = ValidRejection::<io::Error>::Inner(io::Error::new(io::ErrorKind::Other, TEST));
|
||||||
|
assert!(
|
||||||
|
matches!(vr.source(), Some(source) if source.downcast_ref::<io::Error>().is_some())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,8 +42,11 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::{HasValidate, HasValidateArgs};
|
use crate::HasValidate;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
|
use crate::HasValidateArgs;
|
||||||
use axum_yaml::Yaml;
|
use axum_yaml::Yaml;
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
use validator::ValidateArgs;
|
use validator::ValidateArgs;
|
||||||
|
|
||||||
impl<T> HasValidate for Yaml<T> {
|
impl<T> HasValidate for Yaml<T> {
|
||||||
@@ -53,6 +56,7 @@ impl<T> HasValidate for Yaml<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "validator")]
|
||||||
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Yaml<T> {
|
impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Yaml<T> {
|
||||||
type ValidateArgs = T;
|
type ValidateArgs = T;
|
||||||
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
fn get_validate_args(&self) -> &Self::ValidateArgs {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! # Custom extractor validation
|
//! # Custom extractor validation
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
#![cfg(feature = "validator")]
|
||||||
|
|
||||||
use axum::extract::FromRequestParts;
|
use axum::extract::FromRequestParts;
|
||||||
use axum::http::request::Parts;
|
use axum::http::request::Parts;
|
||||||
use axum::response::{IntoResponse, Response};
|
use axum::response::{IntoResponse, Response};
|
||||||
|
|||||||
Reference in New Issue
Block a user