From 3d055d11a3437290f3f1ee31c48962bdc986f2bb Mon Sep 17 00:00:00 2001 From: gengteng Date: Fri, 29 Sep 2023 13:37:55 +0800 Subject: [PATCH] update docs --- CHANGELOG.md | 13 ++++++++ README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 30 ++++++++++++------- 3 files changed, 113 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6a781..69edf5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,19 @@ ### Fixed +## axum-valid 0.9.0 (2023-09-29) + +### Added + +* Introduced the `ValidEx` type to enhance data validation capabilities. +* Added the `Arguments` and `HasValidateArgs` traits to support the use of `ValidEx`. + +### Changed + +* Upgraded `axum_typed_multipart` to version 0.10.0. + +### Fixed + ## axum-valid 0.8.0 (2023-09-19) ### Added diff --git a/README.md b/README.md index 6e88160..6f4e833 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,86 @@ When validation errors occur, the extractor will automatically return 400 with v To see how each extractor can be used with `Valid`, please refer to the example in the [documentation](https://docs.rs/axum-valid) of the corresponding module. +## Argument-Based Validation + +Here's a basic example of using the `ValidEx` extractor to validate data in a `Form` using arguments: + +```rust,no_run +use axum::routing::post; +use axum::{Form, Router}; +use axum_valid::{Arguments, ValidEx}; +use serde::Deserialize; +use std::ops::{RangeFrom, RangeInclusive}; +use validator::{Validate, ValidateArgs, ValidationError}; + +// NOTE: When some fields use custom validation functions with arguments, +// `#[derive(Validate)]` will implement `ValidateArgs` instead of `Validate` for the type. +// The validation arguments will be a tuple of all the field validation args. +// In this example it is (&RangeInclusive, &RangeFrom). +// For more detailed information and understanding of `ValidateArgs` and their argument types, +// please refer to the `validator` crate documentation. +#[derive(Debug, Validate, Deserialize)] +pub struct Pager { + #[validate(custom(function = "validate_page_size", arg = "&'v_a RangeInclusive"))] + pub page_size: usize, + #[validate(custom(function = "validate_page_no", arg = "&'v_a RangeFrom"))] + pub page_no: usize, +} + +fn validate_page_size(v: usize, args: &RangeInclusive) -> Result<(), ValidationError> { + args.contains(&v) + .then_some(()) + .ok_or_else(|| ValidationError::new("page_size is out of range")) +} + +fn validate_page_no(v: usize, args: &RangeFrom) -> Result<(), ValidationError> { + args.contains(&v) + .then_some(()) + .ok_or_else(|| ValidationError::new("page_no is out of range")) +} + +// NOTE: Clone is required +#[derive(Debug, Clone)] +pub struct PagerValidArgs { + page_size_range: RangeInclusive, + page_no_range: RangeFrom, +} + +// NOTE: This implementation allows PagerValidArgs to be the second member of ValidEx, and provides arguments for actual validation. +// The type mapping >::Args represents the combination of validators applied on each field of Pager. +// get() method returns the validating arguments to be used during validation. +impl<'a> Arguments<'a> for PagerValidArgs { + type T = Pager; + + // NOTE: >::Args == (&RangeInclusive, &RangeFrom) + fn get(&'a self) -> >::Args { + (&self.page_size_range, &self.page_no_range) + } +} + +pub async fn pager_from_form_ex(ValidEx(Form(pager), _): ValidEx, PagerValidArgs>) { + assert!((1..=50).contains(&pager.page_size)); + assert!((1..).contains(&pager.page_no)); +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let router = Router::new() + .route("/form", post(pager_from_form_ex)) + .with_state(PagerValidArgs { + page_size_range: 1..=50, + page_no_range: 1.., + }); + // NOTE: The PagerValidArgs can also be stored in a XxxState, + // make sure it implements FromRef. + + axum::Server::bind(&([0u8, 0, 0, 0], 8080).into()) + .serve(router.into_make_service()) + .await?; + Ok(()) +} +``` + ## Features | Feature | Description | Default | Example | Tests | @@ -90,8 +170,7 @@ To see how each extractor can be used with `Valid`, please refer to the example ## Compatibility -* axum-valid 0.7.x works with axum-extra v0.7.x -* axum-valid 0.8.x works with axum-extra v0.8.x +To determine the compatible versions of `axum-valid`, `axum-extra`, `axum-yaml` and other dependencies that work together, please refer to the dependencies listed in the `Cargo.toml` file. The version numbers listed there will indicate the compatible versions. ## License diff --git a/src/lib.rs b/src/lib.rs index 2387c19..d7b21c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -81,17 +81,17 @@ impl Valid { /// # `ValidEx` data extractor /// -/// `ValidEx` can be used with extractors from the various modules. -/// Refer to the examples for `Valid` in each module - the usage of -/// `ValidEx` is similar, except the inner data type implements -/// `ValidateArgs` instead of `Validate`. +/// `ValidEx` can be incorporated with extractors from various modules, similar to `Valid`. +/// Two differences exist between `ValidEx` and `Valid`: /// -/// `ValidateArgs` is usually automatically implemented by validator's -/// derive macros. Refer to validator's documentation for details. +/// - 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. /// -/// Note that the documentation for each module currently only shows -/// examples of `Valid`, and does not demonstrate concrete usage of -/// `ValidEx`, but the usage is analogous. #[derive(Debug, Clone, Copy, Default)] pub struct ValidEx(pub E, pub A); @@ -154,6 +154,10 @@ fn response_builder(ve: ValidationErrors) -> Response { /// 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` 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>; @@ -163,11 +167,15 @@ pub trait Arguments<'a> { /// `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 { - /// Validation errors + /// `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 extractor error + /// `Inner` variant represents potential errors that might occur within the inner extractor. Inner(E), }