update README.md and doc tests
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -8,6 +8,18 @@
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
## axum-valid 0.10.0 (2023-10-09)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Added support for `garde`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Refactored the module structure.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
## axum-valid 0.9.0 (2023-09-29)
|
## axum-valid 0.9.0 (2023-09-29)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "axum-valid"
|
name = "axum-valid"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
description = "Provides validation extractors for your Axum application to validate data using validator, garde, or both."
|
description = "Provides validation extractors for your Axum application to validate data using validator, garde, or both."
|
||||||
authors = ["GengTeng <me@gteng.org>"]
|
authors = ["GengTeng <me@gteng.org>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -21,7 +21,7 @@ categories = [
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["all_types"]
|
features = ["all_types", "garde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.6.20", default-features = false }
|
axum = { version = "0.6.20", default-features = false }
|
||||||
|
|||||||
97
README.md
97
README.md
@@ -126,7 +126,7 @@ To see how each extractor can be used with `Valid`, please refer to the example
|
|||||||
|
|
||||||
## Argument-Based Validation
|
## Argument-Based Validation
|
||||||
|
|
||||||
Here's a basic example of using the `ValidEx` extractor to validate data in a `Form` using arguments:
|
Here are the examples of using the `ValidEx` / `Garde` extractor to validate data in a `Form` using arguments:
|
||||||
|
|
||||||
```rust,no_run
|
```rust,no_run
|
||||||
#[cfg(feature = "validator")]
|
#[cfg(feature = "validator")]
|
||||||
@@ -197,15 +197,63 @@ mod validator_example {
|
|||||||
})
|
})
|
||||||
// NOTE: The PagerValidArgs can also be stored in a XxxState,
|
// NOTE: The PagerValidArgs can also be stored in a XxxState,
|
||||||
// make sure it implements FromRef<XxxState>.
|
// make sure it implements FromRef<XxxState>.
|
||||||
|
// Consider using Arc to reduce deep copying costs.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "garde")]
|
#[cfg(feature = "garde")]
|
||||||
mod garde_example {
|
mod garde_example {
|
||||||
use axum::Router;
|
use axum::routing::post;
|
||||||
|
use axum::{Form, Router};
|
||||||
|
use axum_valid::Garde;
|
||||||
|
use garde::Validate;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::ops::{RangeFrom, RangeInclusive};
|
||||||
|
|
||||||
|
#[derive(Debug, Validate, Deserialize)]
|
||||||
|
#[garde(context(PagerValidContext))]
|
||||||
|
pub struct Pager {
|
||||||
|
#[garde(custom(validate_page_size))]
|
||||||
|
pub page_size: usize,
|
||||||
|
#[garde(custom(validate_page_no))]
|
||||||
|
pub page_no: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_page_size(v: &usize, args: &PagerValidContext) -> garde::Result {
|
||||||
|
args.page_size_range
|
||||||
|
.contains(&v)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or_else(|| garde::Error::new("page_size is out of range"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_page_no(v: &usize, args: &PagerValidContext) -> garde::Result {
|
||||||
|
args.page_no_range
|
||||||
|
.contains(&v)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or_else(|| garde::Error::new("page_no is out of range"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PagerValidContext {
|
||||||
|
page_size_range: RangeInclusive<usize>,
|
||||||
|
page_no_range: RangeFrom<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pager_from_form_garde(Garde(Form(pager)): Garde<Form<Pager>>) {
|
||||||
|
assert!((1..=50).contains(&pager.page_size));
|
||||||
|
assert!((1..).contains(&pager.page_no));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn router() -> Router {
|
pub fn router() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
|
.route("/form", post(pager_from_form_garde))
|
||||||
|
.with_state(PagerValidContext {
|
||||||
|
page_size_range: 1..=50,
|
||||||
|
page_no_range: 1..,
|
||||||
|
})
|
||||||
|
// NOTE: The PagerValidContext can also be stored in a XxxState,
|
||||||
|
// make sure it implements FromRef<XxxState>.
|
||||||
|
// Consider using Arc to reduce deep copying costs.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,26 +276,30 @@ Current module documentation predominantly showcases `Valid` examples, the usage
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
| Feature | Description | Module | Default | Example | Tests |
|
| Feature | Description | Module | Default | Example | Tests |
|
||||||
|------------------|------------------------------------------------------------------------------------------------------|-----------------------------------------|---------|---------|-------|
|
|------------------|---------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|---------|---------|-------|
|
||||||
| default | Enables support for `Path`, `Query`, `Json` and `Form` | [`path`], [`query`], [`json`], [`form`] | ✅ | ✅ | ✅ |
|
| default | Enables `validator` and support for `Query`, `Json` and `Form` | [`validator`], [`query`], [`json`], [`form`] | ✅ | ✅ | ✅ |
|
||||||
| json | Enables support for `Json` | [`json`] | ✅ | ✅ | ✅ |
|
| validator | Enables `validator` | [`validator`] | ✅ | ✅ | ✅ |
|
||||||
| query | Enables support for `Query` | [`query`] | ✅ | ✅ | ✅ |
|
| garde | Enables `garde` | [`garde`] | ❌ | ✅ | ✅ |
|
||||||
| form | Enables support for `Form` | [`form`] | ✅ | ✅ | ✅ |
|
| basic | Enables support for `Query`, `Json` and `Form` | [`query`], [`json`], [`form`] | ✅ | ✅ | ✅ |
|
||||||
| typed_header | Enables support for `TypedHeader` | [`typed_header`] | ❌ | ✅ | ✅ |
|
| json | Enables support for `Json` | [`json`] | ✅ | ✅ | ✅ |
|
||||||
| typed_multipart | Enables support for `TypedMultipart` and `BaseMultipart` from `axum_typed_multipart` | [`typed_multipart`] | ❌ | ✅ | ✅ |
|
| query | Enables support for `Query` | [`query`] | ✅ | ✅ | ✅ |
|
||||||
| msgpack | Enables support for `MsgPack` and `MsgPackRaw` from `axum-msgpack` | [`msgpack`] | ❌ | ✅ | ✅ |
|
| form | Enables support for `Form` | [`form`] | ✅ | ✅ | ✅ |
|
||||||
| yaml | Enables support for `Yaml` from `axum-yaml` | [`yaml`] | ❌ | ✅ | ✅ |
|
| typed_header | Enables support for `TypedHeader` | [`typed_header`] | ❌ | ✅ | ✅ |
|
||||||
| extra | Enables support for `Cached`, `WithRejection` from `axum-extra` | [`extra`] | ❌ | ✅ | ✅ |
|
| typed_multipart | Enables support for `TypedMultipart` and `BaseMultipart` from `axum_typed_multipart` | [`typed_multipart`] | ❌ | ✅ | ✅ |
|
||||||
| extra_typed_path | Enables support for `T: TypedPath` from `axum-extra` | [`extra::typed_path`] | ❌ | ✅ | ✅ |
|
| msgpack | Enables support for `MsgPack` and `MsgPackRaw` from `axum-msgpack` | [`msgpack`] | ❌ | ✅ | ✅ |
|
||||||
| extra_query | Enables support for `Query` from `axum-extra` | [`extra::query`] | ❌ | ✅ | ✅ |
|
| yaml | Enables support for `Yaml` from `axum-yaml` | [`yaml`] | ❌ | ✅ | ✅ |
|
||||||
| extra_form | Enables support for `Form` from `axum-extra` | [`extra::form`] | ❌ | ✅ | ✅ |
|
| extra | Enables support for `Cached`, `WithRejection` from `axum-extra` | [`extra`] | ❌ | ✅ | ✅ |
|
||||||
| extra_protobuf | Enables support for `Protobuf` from `axum-extra` | [`extra::protobuf`] | ❌ | ✅ | ✅ |
|
| extra_typed_path | Enables support for `T: TypedPath` from `axum-extra` | [`extra::typed_path`] | ❌ | ✅ | ✅ |
|
||||||
| all_extra_types | Enables support for all extractors above from `axum-extra` | N/A | ❌ | ✅ | ✅ |
|
| extra_query | Enables support for `Query` from `axum-extra` | [`extra::query`] | ❌ | ✅ | ✅ |
|
||||||
| all_types | Enables support for all extractors above | N/A | ❌ | ✅ | ✅ |
|
| extra_form | Enables support for `Form` from `axum-extra` | [`extra::form`] | ❌ | ✅ | ✅ |
|
||||||
| 422 | Use `422 Unprocessable Entity` instead of `400 Bad Request` as the status code when validation fails | [`VALIDATION_ERROR_STATUS`] | ❌ | ✅ | ✅ |
|
| extra_protobuf | Enables support for `Protobuf` from `axum-extra` | [`extra::protobuf`] | ❌ | ✅ | ✅ |
|
||||||
| into_json | Validation errors will be serialized into JSON format and returned as the HTTP body | N/A | ❌ | ✅ | ✅ |
|
| all_extra_types | Enables support for all extractors above from `axum-extra` | N/A | ❌ | ✅ | ✅ |
|
||||||
| full | Enables all features | N/A | ❌ | ✅ | ✅ |
|
| all_types | Enables support for all extractors above | N/A | ❌ | ✅ | ✅ |
|
||||||
|
| 422 | Use `422 Unprocessable Entity` instead of `400 Bad Request` as the status code when validation fails | [`VALIDATION_ERROR_STATUS`] | ❌ | ✅ | ✅ |
|
||||||
|
| into_json | Validation errors will be serialized into JSON format and returned as the HTTP body | N/A | ❌ | ✅ | ✅ |
|
||||||
|
| full | Enables `all_types`, `422` and `into_json` | N/A | ❌ | ✅ | ✅ |
|
||||||
|
| full_garde | Enables `garde`, `all_types`, `422` and `into_json`. Consider using `default-features = false` to exclude default `validator` support | N/A | ❌ | ✅ | ✅ |
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
@@ -261,6 +313,7 @@ This project is licensed under the MIT License.
|
|||||||
|
|
||||||
* [axum](https://crates.io/crates/axum)
|
* [axum](https://crates.io/crates/axum)
|
||||||
* [validator](https://crates.io/crates/validator)
|
* [validator](https://crates.io/crates/validator)
|
||||||
|
* [garde](https://crates.io/crates/garde)
|
||||||
* [serde](https://crates.io/crates/serde)
|
* [serde](https://crates.io/crates/serde)
|
||||||
* [axum-extra](https://crates.io/crates/axum-extra)
|
* [axum-extra](https://crates.io/crates/axum-extra)
|
||||||
* [axum-yaml](https://crates.io/crates/axum-yaml)
|
* [axum-yaml](https://crates.io/crates/axum-yaml)
|
||||||
|
|||||||
490
src/extra.rs
490
src/extra.rs
@@ -26,54 +26,114 @@
|
|||||||
//! #### Example
|
//! #### Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! #![cfg(feature = "validator")]
|
//! #[cfg(feature = "validator")]
|
||||||
|
//! mod validator_example {
|
||||||
|
//! use axum::extract::FromRequestParts;
|
||||||
|
//! use axum::http::request::Parts;
|
||||||
|
//! use axum::response::{IntoResponse, Response};
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::Router;
|
||||||
|
//! use axum_extra::extract::Cached;
|
||||||
|
//! use axum_valid::Valid;
|
||||||
|
//! use validator::Validate;
|
||||||
//!
|
//!
|
||||||
//! use axum::extract::FromRequestParts;
|
//! pub fn router() -> Router {
|
||||||
//! use axum::http::request::Parts;
|
//! Router::new().route("/cached", post(handler))
|
||||||
//! use axum::response::{IntoResponse, Response};
|
//! }
|
||||||
//! use axum::routing::post;
|
|
||||||
//! use axum::Router;
|
|
||||||
//! use axum_extra::extract::Cached;
|
|
||||||
//! use axum_valid::Valid;
|
|
||||||
//! use validator::Validate;
|
|
||||||
//! #[tokio::main]
|
|
||||||
//! async fn main() -> anyhow::Result<()> {
|
|
||||||
//! let router = Router::new().route("/cached", post(handler));
|
|
||||||
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
|
||||||
//! .serve(router.into_make_service())
|
|
||||||
//! .await?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! async fn handler(Valid(Cached(parameter)): Valid<Cached<Parameter>>) {
|
|
||||||
//! assert!(parameter.validate().is_ok());
|
|
||||||
//! }
|
|
||||||
//! #[derive(Validate, Clone)]
|
|
||||||
//! pub struct Parameter {
|
|
||||||
//! #[validate(range(min = 5, max = 10))]
|
|
||||||
//! pub v0: i32,
|
|
||||||
//! #[validate(length(min = 1, max = 10))]
|
|
||||||
//! pub v1: String,
|
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! pub struct ParameterRejection;
|
//! async fn handler(Valid(Cached(parameter)): Valid<Cached<Parameter>>) {
|
||||||
|
//! assert!(parameter.validate().is_ok());
|
||||||
|
//! }
|
||||||
//!
|
//!
|
||||||
//! impl IntoResponse for ParameterRejection {
|
//! #[derive(Validate, Clone)]
|
||||||
//! fn into_response(self) -> Response {
|
//! pub struct Parameter {
|
||||||
//! todo!()
|
//! #[validate(range(min = 5, max = 10))]
|
||||||
|
//! pub v0: i32,
|
||||||
|
//! #[validate(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub struct ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! impl IntoResponse for ParameterRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[axum::async_trait]
|
||||||
|
//! impl<S> FromRequestParts<S> for Parameter
|
||||||
|
//! where
|
||||||
|
//! S: Send + Sync,
|
||||||
|
//! {
|
||||||
|
//! type Rejection = ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! #[cfg(feature = "garde")]
|
||||||
|
//! mod garde_example {
|
||||||
|
//! use axum::extract::FromRequestParts;
|
||||||
|
//! use axum::http::request::Parts;
|
||||||
|
//! use axum::response::{IntoResponse, Response};
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::Router;
|
||||||
|
//! use axum_extra::extract::Cached;
|
||||||
|
//! use axum_valid::Garde;
|
||||||
|
//! use garde::Validate;
|
||||||
|
//!
|
||||||
|
//! pub fn router() -> Router {
|
||||||
|
//! Router::new().route("/cached", post(handler))
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn handler(Garde(Cached(parameter)): Garde<Cached<Parameter>>) {
|
||||||
|
//! assert!(parameter.validate(&()).is_ok());
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[derive(Validate, Clone)]
|
||||||
|
//! pub struct Parameter {
|
||||||
|
//! #[garde(range(min = 5, max = 10))]
|
||||||
|
//! pub v0: i32,
|
||||||
|
//! #[garde(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub struct ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! impl IntoResponse for ParameterRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[axum::async_trait]
|
||||||
|
//! impl<S> FromRequestParts<S> for Parameter
|
||||||
|
//! where
|
||||||
|
//! S: Send + Sync,
|
||||||
|
//! {
|
||||||
|
//! type Rejection = ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! #[axum::async_trait]
|
//! # #[tokio::main]
|
||||||
//! impl<S> FromRequestParts<S> for Parameter
|
//! # async fn main() -> anyhow::Result<()> {
|
||||||
//! where
|
//! # use axum::Router;
|
||||||
//! S: Send + Sync,
|
//! # let router = Router::new();
|
||||||
//! {
|
//! # #[cfg(feature = "validator")]
|
||||||
//! type Rejection = ParameterRejection;
|
//! # let router = router.nest("/validator", validator_example::router());
|
||||||
//!
|
//! # #[cfg(feature = "garde")]
|
||||||
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
//! # let router = router.nest("/garde", garde_example::router());
|
||||||
//! todo!()
|
//! # axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||||
//! }
|
//! # .serve(router.into_make_service())
|
||||||
//! }
|
//! # .await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### `Valid<WithRejection<T, R>>`
|
//! ### `Valid<WithRejection<T, R>>`
|
||||||
@@ -87,60 +147,125 @@
|
|||||||
//! #### Example
|
//! #### Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! #![cfg(feature = "validator")]
|
//! #[cfg(feature = "validator")]
|
||||||
|
//! mod validator_example {
|
||||||
|
//! use axum::extract::FromRequestParts;
|
||||||
|
//! use axum::http::request::Parts;
|
||||||
|
//! use axum::http::StatusCode;
|
||||||
|
//! use axum::response::{IntoResponse, Response};
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::Router;
|
||||||
|
//! use axum_extra::extract::WithRejection;
|
||||||
|
//! use axum_valid::Valid;
|
||||||
|
//! use validator::Validate;
|
||||||
//!
|
//!
|
||||||
//! use axum::extract::FromRequestParts;
|
//! pub fn router() -> Router {
|
||||||
//! use axum::http::request::Parts;
|
//! Router::new().route("/valid_with_rejection", post(handler))
|
||||||
//! use axum::http::StatusCode;
|
//! }
|
||||||
//! use axum::response::{IntoResponse, Response};
|
|
||||||
//! use axum::routing::post;
|
|
||||||
//! use axum::Router;
|
|
||||||
//! use axum_extra::extract::WithRejection;
|
|
||||||
//! use axum_valid::Valid;
|
|
||||||
//! use validator::Validate;
|
|
||||||
//! #[tokio::main]
|
|
||||||
//! async fn main() -> anyhow::Result<()> {
|
|
||||||
//! let router = Router::new().route("/valid_with_rejection", post(handler));
|
|
||||||
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
|
||||||
//! .serve(router.into_make_service())
|
|
||||||
//! .await?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! async fn handler(
|
|
||||||
//! Valid(WithRejection(parameter, _)): Valid<
|
|
||||||
//! WithRejection<Parameter, ValidWithRejectionRejection>,
|
|
||||||
//! >,
|
|
||||||
//! ) {
|
|
||||||
//! assert!(parameter.validate().is_ok());
|
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! #[derive(Validate)]
|
//! async fn handler(
|
||||||
//! pub struct Parameter {
|
//! Valid(WithRejection(parameter, _)): Valid<
|
||||||
//! #[validate(range(min = 5, max = 10))]
|
//! WithRejection<Parameter, ValidWithRejectionRejection>,
|
||||||
//! pub v0: i32,
|
//! >,
|
||||||
//! #[validate(length(min = 1, max = 10))]
|
//! ) {
|
||||||
//! pub v1: String,
|
//! assert!(parameter.validate().is_ok());
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! pub struct ValidWithRejectionRejection;
|
//! #[derive(Validate)]
|
||||||
|
//! pub struct Parameter {
|
||||||
|
//! #[validate(range(min = 5, max = 10))]
|
||||||
|
//! pub v0: i32,
|
||||||
|
//! #[validate(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
|
//! }
|
||||||
//!
|
//!
|
||||||
//! impl IntoResponse for ValidWithRejectionRejection {
|
//! pub struct ValidWithRejectionRejection;
|
||||||
//! fn into_response(self) -> Response {
|
//!
|
||||||
//! StatusCode::BAD_REQUEST.into_response()
|
//! impl IntoResponse for ValidWithRejectionRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! StatusCode::BAD_REQUEST.into_response()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[axum::async_trait]
|
||||||
|
//! impl<S> FromRequestParts<S> for Parameter
|
||||||
|
//! where
|
||||||
|
//! S: Send + Sync,
|
||||||
|
//! {
|
||||||
|
//! type Rejection = ValidWithRejectionRejection;
|
||||||
|
//!
|
||||||
|
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! #[axum::async_trait]
|
//! #[cfg(feature = "garde")]
|
||||||
//! impl<S> FromRequestParts<S> for Parameter
|
//! mod garde_example {
|
||||||
//! where
|
//! use axum::extract::FromRequestParts;
|
||||||
//! S: Send + Sync,
|
//! use axum::http::request::Parts;
|
||||||
//! {
|
//! use axum::http::StatusCode;
|
||||||
//! type Rejection = ValidWithRejectionRejection;
|
//! use axum::response::{IntoResponse, Response};
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::Router;
|
||||||
|
//! use axum_extra::extract::WithRejection;
|
||||||
|
//! use axum_valid::Garde;
|
||||||
|
//! use garde::Validate;
|
||||||
//!
|
//!
|
||||||
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
//! pub fn router() -> Router {
|
||||||
//! todo!()
|
//! Router::new().route("/valid_with_rejection", post(handler))
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn handler(
|
||||||
|
//! Garde(WithRejection(parameter, _)): Garde<
|
||||||
|
//! WithRejection<Parameter, ValidWithRejectionRejection>,
|
||||||
|
//! >,
|
||||||
|
//! ) {
|
||||||
|
//! assert!(parameter.validate(&()).is_ok());
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[derive(Validate)]
|
||||||
|
//! pub struct Parameter {
|
||||||
|
//! #[garde(range(min = 5, max = 10))]
|
||||||
|
//! pub v0: i32,
|
||||||
|
//! #[garde(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub struct ValidWithRejectionRejection;
|
||||||
|
//!
|
||||||
|
//! impl IntoResponse for ValidWithRejectionRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! StatusCode::BAD_REQUEST.into_response()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[axum::async_trait]
|
||||||
|
//! impl<S> FromRequestParts<S> for Parameter
|
||||||
|
//! where
|
||||||
|
//! S: Send + Sync,
|
||||||
|
//! {
|
||||||
|
//! type Rejection = ValidWithRejectionRejection;
|
||||||
|
//!
|
||||||
|
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
//!
|
||||||
|
//! # #[tokio::main]
|
||||||
|
//! # async fn main() -> anyhow::Result<()> {
|
||||||
|
//! # use axum::Router;
|
||||||
|
//! # let router = Router::new();
|
||||||
|
//! # #[cfg(feature = "validator")]
|
||||||
|
//! # let router = router.nest("/validator", validator_example::router());
|
||||||
|
//! # #[cfg(feature = "garde")]
|
||||||
|
//! # let router = router.nest("/garde", garde_example::router());
|
||||||
|
//! # axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||||
|
//! # .serve(router.into_make_service())
|
||||||
|
//! # .await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### `WithRejection<Valid<T>, R>`
|
//! ### `WithRejection<Valid<T>, R>`
|
||||||
@@ -155,80 +280,153 @@
|
|||||||
//! #### Example
|
//! #### Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! #![cfg(feature = "validator")]
|
//! #[cfg(feature = "validator")]
|
||||||
|
//! mod validator_example {
|
||||||
|
//! use axum::extract::FromRequestParts;
|
||||||
|
//! use axum::http::request::Parts;
|
||||||
|
//! use axum::response::{IntoResponse, Response};
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::Router;
|
||||||
|
//! use axum_extra::extract::WithRejection;
|
||||||
|
//! use axum_valid::{HasValidate, Valid, ValidRejection};
|
||||||
|
//! use validator::Validate;
|
||||||
//!
|
//!
|
||||||
//! use axum::extract::FromRequestParts;
|
//! pub fn router() -> Router {
|
||||||
//! use axum::http::request::Parts;
|
//! Router::new().route("/with_rejection_valid", post(handler))
|
||||||
//! use axum::response::{IntoResponse, Response};
|
//! }
|
||||||
//! use axum::routing::post;
|
|
||||||
//! use axum::Router;
|
|
||||||
//! use axum_extra::extract::WithRejection;
|
|
||||||
//! use axum_valid::{HasValidate, Valid, ValidRejection};
|
|
||||||
//! use validator::Validate;
|
|
||||||
//! #[tokio::main]
|
|
||||||
//! async fn main() -> anyhow::Result<()> {
|
|
||||||
//! let router = Router::new().route("/with_rejection_valid", post(handler));
|
|
||||||
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
|
||||||
//! .serve(router.into_make_service())
|
|
||||||
//! .await?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! async fn handler(
|
|
||||||
//! WithRejection(Valid(parameter), _): WithRejection<
|
|
||||||
//! Valid<Parameter>,
|
|
||||||
//! WithRejectionValidRejection,
|
|
||||||
//! >,
|
|
||||||
//! ) {
|
|
||||||
//! assert!(parameter.validate().is_ok());
|
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! #[derive(Validate)]
|
//! async fn handler(
|
||||||
//! pub struct Parameter {
|
//! WithRejection(Valid(parameter), _): WithRejection<
|
||||||
//! #[validate(range(min = 5, max = 10))]
|
//! Valid<Parameter>,
|
||||||
//! pub v0: i32,
|
//! WithRejectionValidRejection,
|
||||||
//! #[validate(length(min = 1, max = 10))]
|
//! >,
|
||||||
//! pub v1: String,
|
//! ) {
|
||||||
//! }
|
//! assert!(parameter.validate().is_ok());
|
||||||
|
//! }
|
||||||
//!
|
//!
|
||||||
//! impl HasValidate for Parameter {
|
//! #[derive(Validate)]
|
||||||
//! type Validate = Self;
|
//! pub struct Parameter {
|
||||||
|
//! #[validate(range(min = 5, max = 10))]
|
||||||
|
//! pub v0: i32,
|
||||||
|
//! #[validate(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn get_validate(&self) -> &Self::Validate {
|
//! impl HasValidate for Parameter {
|
||||||
//! self
|
//! type Validate = Self;
|
||||||
|
//!
|
||||||
|
//! fn get_validate(&self) -> &Self::Validate {
|
||||||
|
//! self
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub struct ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! impl IntoResponse for ParameterRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[axum::async_trait]
|
||||||
|
//! impl<S> FromRequestParts<S> for Parameter
|
||||||
|
//! where
|
||||||
|
//! S: Send + Sync,
|
||||||
|
//! {
|
||||||
|
//! type Rejection = ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub struct WithRejectionValidRejection;
|
||||||
|
//!
|
||||||
|
//! impl From<ValidRejection<ParameterRejection>> for WithRejectionValidRejection {
|
||||||
|
//! fn from(_inner: ValidRejection<ParameterRejection>) -> Self {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl IntoResponse for WithRejectionValidRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! pub struct ParameterRejection;
|
//! #[cfg(feature = "garde")]
|
||||||
|
//! mod garde_example {
|
||||||
|
//! use axum::extract::FromRequestParts;
|
||||||
|
//! use axum::http::request::Parts;
|
||||||
|
//! use axum::response::{IntoResponse, Response};
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::Router;
|
||||||
|
//! use axum_extra::extract::WithRejection;
|
||||||
|
//! use axum_valid::{HasValidate, Garde, GardeRejection};
|
||||||
|
//! use garde::Validate;
|
||||||
//!
|
//!
|
||||||
//! impl IntoResponse for ParameterRejection {
|
//! pub fn router() -> Router {
|
||||||
//! fn into_response(self) -> Response {
|
//! Router::new().route("/with_rejection_valid", post(handler))
|
||||||
//! todo!()
|
|
||||||
//! }
|
//! }
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! #[axum::async_trait]
|
//! async fn handler(
|
||||||
//! impl<S> FromRequestParts<S> for Parameter
|
//! WithRejection(Garde(parameter), _): WithRejection<
|
||||||
//! where
|
//! Garde<Parameter>,
|
||||||
//! S: Send + Sync,
|
//! WithRejectionGardeRejection,
|
||||||
//! {
|
//! >,
|
||||||
//! type Rejection = ParameterRejection;
|
//! ) {
|
||||||
//!
|
//! assert!(parameter.validate(&()).is_ok());
|
||||||
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
|
||||||
//! todo!()
|
|
||||||
//! }
|
//! }
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! pub struct WithRejectionValidRejection;
|
//! #[derive(Validate)]
|
||||||
//!
|
//! pub struct Parameter {
|
||||||
//! impl From<ValidRejection<ParameterRejection>> for WithRejectionValidRejection {
|
//! #[garde(range(min = 5, max = 10))]
|
||||||
//! fn from(_inner: ValidRejection<ParameterRejection>) -> Self {
|
//! pub v0: i32,
|
||||||
//! todo!()
|
//! #[garde(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
//! }
|
//! }
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! impl IntoResponse for WithRejectionValidRejection {
|
//! impl HasValidate for Parameter {
|
||||||
//! fn into_response(self) -> Response {
|
//! type Validate = Self;
|
||||||
//! todo!()
|
//!
|
||||||
|
//! fn get_validate(&self) -> &Self::Validate {
|
||||||
|
//! self
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub struct ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! impl IntoResponse for ParameterRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[axum::async_trait]
|
||||||
|
//! impl<S> FromRequestParts<S> for Parameter
|
||||||
|
//! where
|
||||||
|
//! S: Send + Sync,
|
||||||
|
//! {
|
||||||
|
//! type Rejection = ParameterRejection;
|
||||||
|
//!
|
||||||
|
//! async fn from_request_parts(_parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! pub struct WithRejectionGardeRejection;
|
||||||
|
//!
|
||||||
|
//! impl From<GardeRejection<ParameterRejection>> for WithRejectionGardeRejection {
|
||||||
|
//! fn from(_inner: GardeRejection<ParameterRejection>) -> Self {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl IntoResponse for WithRejectionGardeRejection {
|
||||||
|
//! fn into_response(self) -> Response {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|||||||
@@ -63,10 +63,10 @@
|
|||||||
//!
|
//!
|
||||||
//! #[derive(Validate, prost::Message)]
|
//! #[derive(Validate, prost::Message)]
|
||||||
//! pub struct Parameter {
|
//! pub struct Parameter {
|
||||||
//! #[validate(range(min = 5, max = 10))]
|
//! #[garde(range(min = 5, max = 10))]
|
||||||
//! #[prost(int32, tag = "1")]
|
//! #[prost(int32, tag = "1")]
|
||||||
//! pub v0: i32,
|
//! pub v0: i32,
|
||||||
//! #[validate(length(min = 1, max = 10))]
|
//! #[garde(length(min = 1, max = 10))]
|
||||||
//! #[prost(string, tag = "2")]
|
//! #[prost(string, tag = "2")]
|
||||||
//! pub v1: String,
|
//! pub v1: String,
|
||||||
//! }
|
//! }
|
||||||
|
|||||||
@@ -12,41 +12,87 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use axum::Router;
|
//! #[cfg(feature = "validator")]
|
||||||
//! use axum_extra::routing::{RouterExt, TypedPath};
|
//! mod validator_example {
|
||||||
//! use axum_valid::{HasValidate, Valid};
|
//! use axum::Router;
|
||||||
//! use serde::Deserialize;
|
//! use axum_extra::routing::{RouterExt, TypedPath};
|
||||||
//! use validator::Validate;
|
//! use axum_valid::{HasValidate, Valid};
|
||||||
|
//! use serde::Deserialize;
|
||||||
|
//! use validator::Validate;
|
||||||
//!
|
//!
|
||||||
//! #[tokio::main]
|
//! pub fn router() -> Router {
|
||||||
//! async fn main() -> anyhow::Result<()> {
|
//! Router::new().typed_get(handler)
|
||||||
//! let router = Router::new().typed_get(handler);
|
//! }
|
||||||
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
|
||||||
//! .serve(router.into_make_service())
|
|
||||||
//! .await?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! async fn handler(parameter: Valid<Parameter>) {
|
//! async fn handler(parameter: Valid<Parameter>) {
|
||||||
//! assert!(parameter.validate().is_ok());
|
//! assert!(parameter.validate().is_ok());
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! #[derive(TypedPath, Deserialize, Validate)]
|
//! #[derive(TypedPath, Deserialize, Validate)]
|
||||||
//! #[typed_path("/extra_typed_path/:v0/:v1")]
|
//! #[typed_path("/extra_typed_path/:v0/:v1")]
|
||||||
//! struct Parameter {
|
//! struct Parameter {
|
||||||
//! #[validate(range(min = 5, max = 10))]
|
//! #[validate(range(min = 5, max = 10))]
|
||||||
//! v0: i32,
|
//! v0: i32,
|
||||||
//! #[validate(length(min = 1, max = 10))]
|
//! #[validate(length(min = 1, max = 10))]
|
||||||
//! v1: String,
|
//! v1: String,
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! impl HasValidate for Parameter {
|
//! impl HasValidate for Parameter {
|
||||||
//! type Validate = Self;
|
//! type Validate = Self;
|
||||||
//!
|
//!
|
||||||
//! fn get_validate(&self) -> &Self::Validate {
|
//! fn get_validate(&self) -> &Self::Validate {
|
||||||
//! self
|
//! self
|
||||||
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[cfg(feature = "garde")]
|
||||||
|
//! mod garde_example {
|
||||||
|
//! use axum::Router;
|
||||||
|
//! use axum_extra::routing::{RouterExt, TypedPath};
|
||||||
|
//! use axum_valid::{HasValidate, Garde};
|
||||||
|
//! use serde::Deserialize;
|
||||||
|
//! use garde::Validate;
|
||||||
|
//!
|
||||||
|
//! pub fn router() -> Router {
|
||||||
|
//! Router::new().typed_get(handler)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn handler(parameter: Garde<Parameter>) {
|
||||||
|
//! assert!(parameter.validate(&()).is_ok());
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[derive(TypedPath, Deserialize, Validate)]
|
||||||
|
//! #[typed_path("/extra_typed_path/:v0/:v1")]
|
||||||
|
//! struct Parameter {
|
||||||
|
//! #[garde(range(min = 5, max = 10))]
|
||||||
|
//! v0: i32,
|
||||||
|
//! #[garde(length(min = 1, max = 10))]
|
||||||
|
//! v1: String,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl HasValidate for Parameter {
|
||||||
|
//! type Validate = Self;
|
||||||
|
//!
|
||||||
|
//! fn get_validate(&self) -> &Self::Validate {
|
||||||
|
//! self
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! # #[tokio::main]
|
||||||
|
//! # async fn main() -> anyhow::Result<()> {
|
||||||
|
//! # use axum::Router;
|
||||||
|
//! # let router = Router::new();
|
||||||
|
//! # #[cfg(feature = "validator")]
|
||||||
|
//! # let router = router.nest("/validator", validator_example::router());
|
||||||
|
//! # #[cfg(feature = "garde")]
|
||||||
|
//! # let router = router.nest("/garde", garde_example::router());
|
||||||
|
//! # axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||||
|
//! # .serve(router.into_make_service())
|
||||||
|
//! # .await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#[cfg(feature = "garde")]
|
#[cfg(feature = "garde")]
|
||||||
|
|||||||
45
src/garde.rs
45
src/garde.rs
@@ -1,4 +1,9 @@
|
|||||||
//! # Garde support
|
//! # Garde support
|
||||||
|
//!
|
||||||
|
//! ## Feature
|
||||||
|
//!
|
||||||
|
//! Enable the `garde` feature to use `Garde<E>`.
|
||||||
|
//!
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
@@ -40,7 +45,7 @@ impl<T: Display> Display for Garde<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<E> Garde<E> {
|
impl<E> Garde<E> {
|
||||||
/// Consumes the `ValidEx` and returns the validated data within.
|
/// Consumes the `Garde` and returns the validated data within.
|
||||||
///
|
///
|
||||||
/// This returns the `E` type which represents the data that has been
|
/// This returns the `E` type which represents the data that has been
|
||||||
/// successfully validated.
|
/// successfully validated.
|
||||||
@@ -60,14 +65,14 @@ fn response_builder(ve: garde::Report) -> Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ValidRejection` is returned when the `Valid` extractor fails.
|
/// `GardeRejection` is returned when the `Garde` extractor fails.
|
||||||
///
|
///
|
||||||
/// This enumeration captures two types of errors that can occur when using `Valid`: errors related to the validation
|
/// This enumeration captures two types of errors that can occur when using `Garde`: errors related to the validation
|
||||||
/// logic itself (encapsulated in `Valid`), and errors that may arise within the inner extractor (represented by `Inner`).
|
/// logic itself (encapsulated in `Garde`), and errors that may arise within the inner extractor (represented by `Inner`).
|
||||||
///
|
///
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GardeRejection<E> {
|
pub enum GardeRejection<E> {
|
||||||
/// `Valid` variant captures errors related to the validation logic. It contains `garde::Report`
|
/// `Report` variant captures errors related to the validation logic. It contains `garde::Report`
|
||||||
/// which is a collection of validation failures for each field.
|
/// which is a collection of validation failures for each field.
|
||||||
Report(garde::Report),
|
Report(garde::Report),
|
||||||
/// `Inner` variant represents potential errors that might occur within the inner extractor.
|
/// `Inner` variant represents potential errors that might occur within the inner extractor.
|
||||||
@@ -152,6 +157,8 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use garde::{Path, Report};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
const GARDE: &str = "garde";
|
const GARDE: &str = "garde";
|
||||||
|
|
||||||
@@ -163,6 +170,34 @@ mod tests {
|
|||||||
inner.push_str(GARDE);
|
inner.push_str(GARDE);
|
||||||
v.deref_mut().push_str(GARDE);
|
v.deref_mut().push_str(GARDE);
|
||||||
assert_eq!(&inner, v.deref());
|
assert_eq!(&inner, v.deref());
|
||||||
|
println!("{}", v);
|
||||||
assert_eq!(inner, v.into_inner());
|
assert_eq!(inner, v.into_inner());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_error() {
|
||||||
|
// ValidRejection::Valid Display
|
||||||
|
let mut report = Report::new();
|
||||||
|
report.append(Path::empty(), garde::Error::new(GARDE));
|
||||||
|
let s = report.to_string();
|
||||||
|
let vr = GardeRejection::<String>::Report(report);
|
||||||
|
assert_eq!(vr.to_string(), s);
|
||||||
|
|
||||||
|
// ValidRejection::Inner Display
|
||||||
|
let inner = String::from(GARDE);
|
||||||
|
let vr = GardeRejection::<String>::Inner(inner.clone());
|
||||||
|
assert_eq!(inner.to_string(), vr.to_string());
|
||||||
|
|
||||||
|
// ValidRejection::Valid Error
|
||||||
|
let mut report = Report::new();
|
||||||
|
report.append(Path::empty(), garde::Error::new(GARDE));
|
||||||
|
let vr = GardeRejection::<io::Error>::Report(report);
|
||||||
|
assert!(matches!(vr.source(), Some(source) if source.downcast_ref::<Report>().is_some()));
|
||||||
|
|
||||||
|
// ValidRejection::Valid Error
|
||||||
|
let vr = GardeRejection::<io::Error>::Inner(io::Error::new(io::ErrorKind::Other, GARDE));
|
||||||
|
assert!(
|
||||||
|
matches!(vr.source(), Some(source) if source.downcast_ref::<io::Error>().is_some())
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,55 +12,111 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! #![cfg(feature = "validator")]
|
//! #[cfg(feature = "validator")]
|
||||||
|
//! mod validator_example {
|
||||||
|
//! use axum::headers::{Error, Header, HeaderValue};
|
||||||
|
//! use axum::http::HeaderName;
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::{Router, TypedHeader};
|
||||||
|
//! use axum_valid::Valid;
|
||||||
|
//! use validator::Validate;
|
||||||
//!
|
//!
|
||||||
//! use axum::headers::{Error, Header, HeaderValue};
|
//! pub fn router() -> Router {
|
||||||
//! use axum::http::HeaderName;
|
//! Router::new().route("/typed_header", post(handler))
|
||||||
//! use axum::routing::post;
|
|
||||||
//! use axum::{Router, TypedHeader};
|
|
||||||
//! use axum_valid::Valid;
|
|
||||||
//! use validator::Validate;
|
|
||||||
//!
|
|
||||||
//! #[tokio::main]
|
|
||||||
//! async fn main() -> anyhow::Result<()> {
|
|
||||||
//! let router = Router::new().route("/typed_header", post(handler));
|
|
||||||
//! axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
|
||||||
//! .serve(router.into_make_service())
|
|
||||||
//! .await?;
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! async fn handler(Valid(TypedHeader(parameter)): Valid<TypedHeader<Parameter>>) {
|
|
||||||
//! assert!(parameter.validate().is_ok());
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[derive(Validate)]
|
|
||||||
//! pub struct Parameter {
|
|
||||||
//! #[validate(range(min = 5, max = 10))]
|
|
||||||
//! pub v0: i32,
|
|
||||||
//! #[validate(length(min = 1, max = 10))]
|
|
||||||
//! pub v1: String,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! static HEADER_NAME: HeaderName = HeaderName::from_static("my-header");
|
|
||||||
//!
|
|
||||||
//! impl Header for Parameter {
|
|
||||||
//! fn name() -> &'static HeaderName {
|
|
||||||
//! &HEADER_NAME
|
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn decode<'i, I>(_values: &mut I) -> Result<Self, Error>
|
//! async fn handler(Valid(TypedHeader(parameter)): Valid<TypedHeader<Parameter>>) {
|
||||||
//! where
|
//! assert!(parameter.validate().is_ok());
|
||||||
//! Self: Sized,
|
|
||||||
//! I: Iterator<Item = &'i HeaderValue>,
|
|
||||||
//! {
|
|
||||||
//! todo!()
|
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn encode<E: Extend<HeaderValue>>(&self, _values: &mut E) {
|
//! #[derive(Validate)]
|
||||||
//! todo!()
|
//! pub struct Parameter {
|
||||||
|
//! #[validate(range(min = 5, max = 10))]
|
||||||
|
//! pub v0: i32,
|
||||||
|
//! #[validate(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! static HEADER_NAME: HeaderName = HeaderName::from_static("my-header");
|
||||||
|
//!
|
||||||
|
//! impl Header for Parameter {
|
||||||
|
//! fn name() -> &'static HeaderName {
|
||||||
|
//! &HEADER_NAME
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn decode<'i, I>(_values: &mut I) -> Result<Self, Error>
|
||||||
|
//! where
|
||||||
|
//! Self: Sized,
|
||||||
|
//! I: Iterator<Item = &'i HeaderValue>,
|
||||||
|
//! {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn encode<E: Extend<HeaderValue>>(&self, _values: &mut E) {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[cfg(feature = "garde")]
|
||||||
|
//! mod garde_example {
|
||||||
|
//! use axum::headers::{Error, Header, HeaderValue};
|
||||||
|
//! use axum::http::HeaderName;
|
||||||
|
//! use axum::routing::post;
|
||||||
|
//! use axum::{Router, TypedHeader};
|
||||||
|
//! use axum_valid::Garde;
|
||||||
|
//! use garde::Validate;
|
||||||
|
//!
|
||||||
|
//! pub fn router() -> Router {
|
||||||
|
//! Router::new().route("/typed_header", post(handler))
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! async fn handler(Garde(TypedHeader(parameter)): Garde<TypedHeader<Parameter>>) {
|
||||||
|
//! assert!(parameter.validate(&()).is_ok());
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[derive(Validate)]
|
||||||
|
//! pub struct Parameter {
|
||||||
|
//! #[garde(range(min = 5, max = 10))]
|
||||||
|
//! pub v0: i32,
|
||||||
|
//! #[garde(length(min = 1, max = 10))]
|
||||||
|
//! pub v1: String,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! static HEADER_NAME: HeaderName = HeaderName::from_static("my-header");
|
||||||
|
//!
|
||||||
|
//! impl Header for Parameter {
|
||||||
|
//! fn name() -> &'static HeaderName {
|
||||||
|
//! &HEADER_NAME
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn decode<'i, I>(_values: &mut I) -> Result<Self, Error>
|
||||||
|
//! where
|
||||||
|
//! Self: Sized,
|
||||||
|
//! I: Iterator<Item = &'i HeaderValue>,
|
||||||
|
//! {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn encode<E: Extend<HeaderValue>>(&self, _values: &mut E) {
|
||||||
|
//! todo!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! # #[tokio::main]
|
||||||
|
//! # async fn main() -> anyhow::Result<()> {
|
||||||
|
//! # use axum::Router;
|
||||||
|
//! # let router = Router::new();
|
||||||
|
//! # #[cfg(feature = "validator")]
|
||||||
|
//! # let router = router.nest("/validator", validator_example::router());
|
||||||
|
//! # #[cfg(feature = "garde")]
|
||||||
|
//! # let router = router.nest("/garde", garde_example::router());
|
||||||
|
//! # axum::Server::bind(&([0u8, 0, 0, 0], 8080).into())
|
||||||
|
//! # .serve(router.into_make_service())
|
||||||
|
//! # .await?;
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::HasValidate;
|
use crate::HasValidate;
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
//! # Validator support
|
//! # Validator support
|
||||||
|
//!
|
||||||
|
//! ## Feature
|
||||||
|
//!
|
||||||
|
//! Enable the `validator` feature (enabled by default) to use `Valid<E>` and `ValidEx<E, A>`.
|
||||||
|
//!
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
@@ -287,6 +292,7 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::fmt::Formatter;
|
||||||
use std::io;
|
use std::io;
|
||||||
use validator::ValidationError;
|
use validator::ValidationError;
|
||||||
const TEST: &str = "test";
|
const TEST: &str = "test";
|
||||||
@@ -299,6 +305,7 @@ pub mod tests {
|
|||||||
inner.push_str(TEST);
|
inner.push_str(TEST);
|
||||||
v.deref_mut().push_str(TEST);
|
v.deref_mut().push_str(TEST);
|
||||||
assert_eq!(&inner, v.deref());
|
assert_eq!(&inner, v.deref());
|
||||||
|
println!("{}", v);
|
||||||
assert_eq!(inner, v.into_inner());
|
assert_eq!(inner, v.into_inner());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,12 +323,18 @@ pub mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Validate)]
|
#[derive(Debug, Validate)]
|
||||||
struct Data {
|
struct Data {
|
||||||
#[validate(custom(function = "validate", arg = "i32"))]
|
#[validate(custom(function = "validate", arg = "i32"))]
|
||||||
v: i32,
|
v: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Data {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct DataVA {
|
struct DataVA {
|
||||||
a: i32,
|
a: i32,
|
||||||
}
|
}
|
||||||
@@ -337,6 +350,7 @@ pub mod tests {
|
|||||||
let data = Data { v: 12 };
|
let data = Data { v: 12 };
|
||||||
let args = DataVA { a: 123 };
|
let args = DataVA { a: 123 };
|
||||||
let ve = ValidEx(data, args);
|
let ve = ValidEx(data, args);
|
||||||
|
println!("{}", ve);
|
||||||
assert_eq!(ve.v, 12);
|
assert_eq!(ve.v, 12);
|
||||||
let a = ve.arguments();
|
let a = ve.arguments();
|
||||||
assert_eq!(a, 123);
|
assert_eq!(a, 123);
|
||||||
|
|||||||
Reference in New Issue
Block a user