From e8888c75e994de7f7ecdd85093029edd72308e11 Mon Sep 17 00:00:00 2001 From: gengteng Date: Tue, 28 Nov 2023 16:38:58 +0800 Subject: [PATCH] build(deps): update axum requirement from 0.6.x to 0.7.x --- Cargo.toml | 36 ++---- README.md | 111 +++++++++-------- src/extra.rs | 12 +- src/extra/form.rs | 9 +- src/extra/protobuf.rs | 6 +- src/extra/query.rs | 6 +- src/extra/typed_path.rs | 6 +- src/form.rs | 6 +- src/garde.rs | 12 +- src/garde/test.rs | 65 +++++----- src/json.rs | 6 +- src/lib.rs | 9 +- src/msgpack.rs | 263 ---------------------------------------- src/path.rs | 6 +- src/query.rs | 6 +- src/typed_header.rs | 40 ++++-- src/typed_multipart.rs | 205 ------------------------------- src/validator.rs | 21 ++-- src/validator/test.rs | 73 +++++------ src/validify.rs | 39 +++--- src/validify/test.rs | 65 +++++----- src/yaml.rs | 176 --------------------------- tests/custom.rs | 38 +++--- 23 files changed, 289 insertions(+), 927 deletions(-) delete mode 100644 src/msgpack.rs delete mode 100644 src/typed_multipart.rs delete mode 100644 src/yaml.rs diff --git a/Cargo.toml b/Cargo.toml index 543d5c9..8092ac4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "axum-valid" -version = "0.11.0" +version = "0.12.0-alpha" description = "Provides validation extractors for your Axum application, allowing you to validate data using validator, garde, validify or all of them." authors = ["GengTeng "] license = "MIT" @@ -25,38 +25,23 @@ edition = "2021" features = ["full"] [dependencies] -axum = { version = "0.6.20", default-features = false } -garde = { version = "0.16.0", optional = true } +axum = { version = "0.7.1", default-features = false } +garde = { version = "0.16.3", optional = true } validator = { version = "0.16.1", optional = true} validify = { version = "1.0.12", optional = true } -[dependencies.axum_typed_multipart] -version = "0.10.0" -default-features = false -optional = true - -[dependencies.axum-msgpack] -version = "0.3.0" -default-features = false -optional = true - -[dependencies.axum-yaml] -version = "0.3.0" -default-features = false -optional = true - [dependencies.axum-extra] -version = "0.8.0" +version = "0.9.0" default-features = false optional = true [dependencies.serde] -version = "1.0.189" +version = "1.0.193" optional = true [dev-dependencies] anyhow = "1.0.72" -axum = { version = "0.6.20", features = ["macros"] } +axum = { version = "0.7.1", features = ["macros"] } tokio = { version = "1.33.0", features = ["full"] } hyper = { version = "0.14.27", features = ["full"] } reqwest = { version = "0.11.22", features = ["json", "multipart"] } @@ -78,10 +63,7 @@ validify = ["dep:validify"] json = ["axum/json"] form = ["axum/form"] query = ["axum/query"] -typed_header = ["axum/headers"] -typed_multipart = ["dep:axum_typed_multipart"] -msgpack = ["dep:axum-msgpack"] -yaml = ["dep:axum-yaml"] +typed_header = ["extra", "axum-extra/typed-header"] into_json = ["json", "dep:serde"] 422 = [] extra = ["dep:axum-extra"] @@ -89,8 +71,8 @@ extra_typed_path = ["extra", "axum-extra/typed-routing"] extra_query = ["extra", "axum-extra/query"] extra_form = ["extra", "axum-extra/form"] extra_protobuf = ["extra", "axum-extra/protobuf"] -all_extra_types = ["extra", "extra_typed_path", "extra_query", "extra_form", "extra_protobuf"] -all_types = ["json", "form", "query", "typed_header", "typed_multipart", "msgpack", "yaml", "all_extra_types"] +all_extra_types = ["extra", "typed_header", "extra_typed_path", "extra_query", "extra_form", "extra_protobuf"] +all_types = ["json", "form", "query", "all_extra_types"] full_validator = ["validator", "all_types", "422", "into_json"] full_garde = ["garde", "all_types", "422", "into_json"] full_validify = ["validify", "all_types", "422", "into_json"] diff --git a/README.md b/README.md index 1f3bf3a..43cb1df 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ ## 📑 Overview +**Note: This is an alpha version. It currently supports extractors from Axum 0.7 and Axum-extra 0.9 only. Support for extractors from `axum-yaml`, `axum-msgpack`, and `axum_typed_multipart` will be provided once their dependencies are updated.** + **axum-valid** is a library that provides data validation extractors for the Axum web framework. It integrates **validator**, **garde** and **validify**, three popular validation crates in the Rust ecosystem, to offer convenient validation and data handling extractors for Axum applications. ## 🚀 Basic usage @@ -26,12 +28,14 @@ cargo add axum-valid * Example ```rust,ignore -use validator::Validate; -use serde::Deserialize; -use axum_valid::Valid; use axum::extract::Query; -use axum::{Json, Router}; use axum::routing::{get, post}; +use axum::{Json, Router}; +use axum_valid::Valid; +use serde::Deserialize; +use std::net::SocketAddr; +use tokio::net::TcpListener; +use validator::Validate; #[derive(Debug, Validate, Deserialize)] pub struct Pager { @@ -41,16 +45,12 @@ pub struct Pager { pub page_no: usize, } -pub async fn pager_from_query( - Valid(Query(pager)): Valid>, -) { +pub async fn pager_from_query(Valid(Query(pager)): Valid>) { assert!((1..=50).contains(&pager.page_size)); assert!((1..).contains(&pager.page_no)); } -pub async fn pager_from_json( - pager: Valid>, -) { +pub async fn pager_from_json(pager: Valid>) { assert!((1..=50).contains(&pager.page_size)); assert!((1..).contains(&pager.page_no)); // NOTE: all extractors provided support automatic dereferencing @@ -62,9 +62,8 @@ async fn main() -> anyhow::Result<()> { let router = Router::new() .route("/query", get(pager_from_query)) .route("/json", post(pager_from_json)); - axum::Server::bind(&([0u8, 0, 0, 0], 8080).into()) - .serve(router.into_make_service()) - .await?; + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` @@ -90,6 +89,8 @@ use axum::{Json, Router}; use axum_valid::Garde; use garde::Validate; use serde::Deserialize; +use std::net::SocketAddr; +use tokio::net::TcpListener; #[derive(Debug, Validate, Deserialize)] pub struct Pager { @@ -132,12 +133,10 @@ async fn main() -> anyhow::Result<()> { state_field: 1, without_validation_arguments: (), }); - axum::Server::bind(&([0u8, 0, 0, 0], 8080).into()) - .serve(router.into_make_service()) - .await?; + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + axum::serve(listener, router.into_make_service()).await?; Ok(()) } - ``` ### 📦 `Validated`, `Modified`, `Validified` and `ValidifiedByRef` @@ -162,9 +161,10 @@ cargo add axum-valid --features validify,basic,typed_multipart --no-default-feat use axum::extract::Query; use axum::routing::{get, post}; use axum::{Form, Json, Router}; -use axum_typed_multipart::{TryFromMultipart, TypedMultipart}; use axum_valid::{Modified, Validated, Validified, ValidifiedByRef}; use serde::Deserialize; +use std::net::SocketAddr; +use tokio::net::TcpListener; use validify::{Validate, Validify}; #[derive(Debug, Validify, Deserialize)] @@ -206,25 +206,32 @@ pub async fn parameters_from_form(parameters: Validified>) { assert!(parameters.validate().is_ok()); } -// NOTE: TypedMultipart doesn't using serde::Deserialize to construct data -// we should use ValidifiedByRef instead of Validified -#[derive(Debug, Validify, TryFromMultipart)] -pub struct FormData { - #[modify(lowercase)] - #[validate(length(min = 1, max = 50))] - pub v0: String, - #[modify(trim)] - #[validate(length(min = 1, max = 100))] - pub v1: String, +pub async fn parameters_from_form_by_ref(parameters: ValidifiedByRef>) { + assert_eq!(parameters.v0, parameters.v0.to_lowercase()); + assert_eq!(parameters.v1, parameters.v1.trim()); + assert!(parameters.validate().is_ok()); } -pub async fn parameters_from_typed_multipart( - ValidifiedByRef(TypedMultipart(data)): ValidifiedByRef>, -) { - assert_eq!(data.v0, data.v0.to_lowercase()); - assert_eq!(data.v1, data.v1.trim()); - assert!(data.validate().is_ok()); -} +// WARN: TypedMultipart will be supported in the stable release. +// NOTE: TypedMultipart doesn't using serde::Deserialize to construct data +// we should use ValidifiedByRef instead of Validified +// #[derive(Debug, Validify, TryFromMultipart)] +// pub struct FormData { +// #[modify(lowercase)] +// #[validate(length(min = 1, max = 50))] +// pub v0: String, +// #[modify(trim)] +// #[validate(length(min = 1, max = 100))] +// pub v1: String, +// } +// +// pub async fn parameters_from_typed_multipart( +// ValidifiedByRef(TypedMultipart(data)): ValidifiedByRef>, +// ) { +// assert_eq!(data.v0, data.v0.to_lowercase()); +// assert_eq!(data.v1, data.v1.trim()); +// assert!(data.validate().is_ok()); +// } #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -232,10 +239,10 @@ async fn main() -> anyhow::Result<()> { .route("/validated", get(pager_from_query)) .route("/modified", post(parameters_from_json)) .route("/validified", post(parameters_from_form)) - .route("/validified_by_ref", post(parameters_from_typed_multipart)); - axum::Server::bind(&([0u8, 0, 0, 0], 8080).into()) - .serve(router.into_make_service()) - .await?; + .route("/validified_by_ref", post(parameters_from_form_by_ref)); + // .route("/validified_by_ref", post(parameters_from_typed_multipart)); + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` @@ -261,7 +268,9 @@ use axum::routing::post; use axum::{Form, Router}; use axum_valid::{Arguments, ValidEx}; use serde::Deserialize; +use std::net::SocketAddr; use std::ops::{RangeFrom, RangeInclusive}; +use tokio::net::TcpListener; use validator::{Validate, ValidateArgs, ValidationError}; // NOTE: When some fields use custom validation functions with arguments, @@ -324,12 +333,10 @@ async fn main() -> anyhow::Result<()> { // 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?; + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + axum::serve(listener, router.into_make_service()).await?; Ok(()) } - ``` ### 📦 `Garde` @@ -337,9 +344,9 @@ async fn main() -> anyhow::Result<()> { * Install ```shell -cargo add validator --features derive -cargo add axum-valid -# validator is enabled by default +cargo add garde +cargo add axum-valid --features garde,basic --no-default-features +# excluding validator ``` * Example @@ -350,7 +357,9 @@ use axum::{Form, Router}; use axum_valid::Garde; use garde::Validate; use serde::Deserialize; +use std::net::SocketAddr; use std::ops::{RangeFrom, RangeInclusive}; +use tokio::net::TcpListener; #[derive(Debug, Validate, Deserialize)] #[garde(context(PagerValidContext))] @@ -397,9 +406,8 @@ async fn main() -> anyhow::Result<()> { // NOTE: The PagerValidContext can also be stored in a XxxState, // make sure it implements FromRef. // Consider using Arc to reduce deep copying costs. - axum::Server::bind(&([0u8, 0, 0, 0], 8080).into()) - .serve(router.into_make_service()) - .await?; + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + axum::serve(listener, router.into_make_service()).await?; Ok(()) } ``` @@ -431,10 +439,7 @@ Current module documentation predominantly showcases `Valid` examples, the usage | json | Enables support for `Json` | [`json`] | ✅ | ✅ | ✅ | | query | Enables support for `Query` | [`query`] | ✅ | ✅ | ✅ | | form | Enables support for `Form` | [`form`] | ✅ | ✅ | ✅ | -| typed_header | Enables support for `TypedHeader` | [`typed_header`] | ❌ | ✅ | ✅ | -| typed_multipart | Enables support for `TypedMultipart` and `BaseMultipart` from `axum_typed_multipart` | [`typed_multipart`] | ❌ | ✅ | ✅ | -| msgpack | Enables support for `MsgPack` and `MsgPackRaw` from `axum-msgpack` | [`msgpack`] | ❌ | ✅ | ✅ | -| yaml | Enables support for `Yaml` from `axum-yaml` | [`yaml`] | ❌ | ✅ | ✅ | +| typed_header | Enables support for `TypedHeader` from `axum-extra` | [`typed_header`] | ❌ | ✅ | ✅ | | extra | Enables support for `Cached`, `WithRejection` from `axum-extra` | [`extra`] | ❌ | ✅ | ✅ | | extra_typed_path | Enables support for `T: TypedPath` from `axum-extra` | [`extra::typed_path`] | ❌ | ✅ | ✅ | | extra_query | Enables support for `Query` from `axum-extra` | [`extra::query`] | ❌ | ✅ | ✅ | diff --git a/src/extra.rs b/src/extra.rs index 3aa901d..e2efe19 100644 --- a/src/extra.rs +++ b/src/extra.rs @@ -123,14 +123,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } @@ -255,14 +257,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/extra/form.rs b/src/extra/form.rs index 2e2a044..2bf0946 100644 --- a/src/extra/form.rs +++ b/src/extra/form.rs @@ -70,14 +70,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } @@ -135,8 +137,9 @@ impl crate::HasValidify for Form { #[cfg(test)] mod tests { use crate::tests::{ValidTest, ValidTestParameter}; + use axum::http::StatusCode; use axum_extra::extract::Form; - use reqwest::{RequestBuilder, StatusCode}; + use reqwest::RequestBuilder; use serde::Serialize; impl ValidTest for Form { diff --git a/src/extra/protobuf.rs b/src/extra/protobuf.rs index f1403d7..c48f816 100644 --- a/src/extra/protobuf.rs +++ b/src/extra/protobuf.rs @@ -74,14 +74,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/extra/query.rs b/src/extra/query.rs index fcaeeb5..b53dfea 100644 --- a/src/extra/query.rs +++ b/src/extra/query.rs @@ -70,14 +70,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/extra/typed_path.rs b/src/extra/typed_path.rs index 22d7bba..bb1c825 100644 --- a/src/extra/typed_path.rs +++ b/src/extra/typed_path.rs @@ -82,14 +82,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/form.rs b/src/form.rs index 8a25622..6f7e229 100644 --- a/src/form.rs +++ b/src/form.rs @@ -70,14 +70,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/garde.rs b/src/garde.rs index 6044464..71610c1 100644 --- a/src/garde.rs +++ b/src/garde.rs @@ -10,9 +10,8 @@ pub mod test; use crate::{HasValidate, ValidationRejection}; use axum::async_trait; -use axum::extract::{FromRef, FromRequest, FromRequestParts}; +use axum::extract::{FromRef, FromRequest, FromRequestParts, Request}; use axum::http::request::Parts; -use axum::http::Request; use garde::{Report, Validate}; use std::fmt::{Display, Formatter}; use std::ops::{Deref, DerefMut}; @@ -69,17 +68,16 @@ impl From for GardeRejection { } #[async_trait] -impl FromRequest for Garde +impl FromRequest for Garde where State: Send + Sync, - Body: Send + Sync + 'static, Context: Send + Sync + FromRef, - Extractor: HasValidate + FromRequest, + Extractor: HasValidate + FromRequest, ::Validate: garde::Validate, { - type Rejection = GardeRejection<>::Rejection>; + type Rejection = GardeRejection<>::Rejection>; - async fn from_request(req: Request, state: &State) -> Result { + async fn from_request(req: Request, state: &State) -> Result { let context: Context = FromRef::from_ref(state); let inner = Extractor::from_request(req, state) .await diff --git a/src/garde/test.rs b/src/garde/test.rs index 43c0198..f0e8656 100644 --- a/src/garde/test.rs +++ b/src/garde/test.rs @@ -3,16 +3,18 @@ use crate::tests::{ValidTest, ValidTestParameter}; use crate::{Garde, HasValidate, VALIDATION_ERROR_STATUS}; use axum::extract::{FromRef, Path, Query}; +use axum::http::StatusCode; use axum::routing::{get, post}; use axum::{Form, Json, Router}; use garde::Validate; use hyper::Method; use once_cell::sync::Lazy; -use reqwest::{StatusCode, Url}; +use reqwest::Url; use serde::{Deserialize, Serialize}; use std::any::type_name; use std::net::SocketAddr; use std::ops::Deref; +use tokio::net::TcpListener; #[derive(Clone, Deserialize, Serialize, Validate, Eq, PartialEq)] #[cfg_attr(feature = "extra_protobuf", derive(prost::Message))] @@ -140,15 +142,14 @@ async fn test_main() -> anyhow::Result<()> { let router = router.with_state(MyState::default()); - let server = axum::Server::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))) - .serve(router.into_make_service()); - let server_addr = server.local_addr(); + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + let server_addr = listener.local_addr()?; + let server = axum::serve(listener, router.into_make_service()); println!("Axum server address: {}.", server_addr); - let (server_guard, close) = tokio::sync::oneshot::channel::<()>(); - let server_handle = tokio::spawn(server.with_graceful_shutdown(async move { - let _ = close.await; - })); + tokio::spawn(async move { + let _ = server.await; + }); let server_url = format!("http://{}", server_addr); let test_executor = TestExecutor::from(Url::parse(&format!("http://{}", server_addr))?); @@ -168,8 +169,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - valid_path_response.status(), - StatusCode::OK, + valid_path_response.status().as_u16(), + StatusCode::OK.as_u16(), "Valid '{}' test failed.", path_type_name ); @@ -180,8 +181,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - error_path_response.status(), - StatusCode::BAD_REQUEST, + error_path_response.status().as_u16(), + StatusCode::BAD_REQUEST.as_u16(), "Error '{}' test failed.", path_type_name ); @@ -195,8 +196,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - invalid_path_response.status(), - VALIDATION_ERROR_STATUS, + invalid_path_response.status().as_u16(), + VALIDATION_ERROR_STATUS.as_u16(), "Invalid '{}' test failed.", path_type_name ); @@ -225,7 +226,7 @@ async fn test_main() -> anyhow::Result<()> { #[cfg(feature = "typed_header")] { - use axum::TypedHeader; + use axum_extra::typed_header::TypedHeader; // Garde test_executor .execute::>( @@ -296,8 +297,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - valid_extra_typed_path_response.status(), - StatusCode::OK, + valid_extra_typed_path_response.status().as_u16(), + StatusCode::OK.as_u16(), "Garde '{}' test failed.", extra_typed_path_type_name ); @@ -308,8 +309,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - error_extra_typed_path_response.status(), - StatusCode::BAD_REQUEST, + error_extra_typed_path_response.status().as_u16(), + StatusCode::BAD_REQUEST.as_u16(), "Error '{}' test failed.", extra_typed_path_type_name ); @@ -323,8 +324,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - invalid_extra_typed_path_response.status(), - VALIDATION_ERROR_STATUS, + invalid_extra_typed_path_response.status().as_u16(), + VALIDATION_ERROR_STATUS.as_u16(), "Invalid '{}' test failed.", extra_typed_path_type_name ); @@ -387,8 +388,6 @@ async fn test_main() -> anyhow::Result<()> { .await?; } - drop(server_guard); - server_handle.await??; Ok(()) } @@ -421,8 +420,8 @@ impl TestExecutor { let valid_builder = self.client.request(method.clone(), url.clone()); let valid_response = T::set_valid_request(valid_builder).send().await?; assert_eq!( - valid_response.status(), - StatusCode::OK, + valid_response.status().as_u16(), + StatusCode::OK.as_u16(), "Garde '{}' test failed.", type_name ); @@ -430,8 +429,8 @@ impl TestExecutor { let error_builder = self.client.request(method.clone(), url.clone()); let error_response = T::set_error_request(error_builder).send().await?; assert_eq!( - error_response.status(), - T::ERROR_STATUS_CODE, + error_response.status().as_u16(), + T::ERROR_STATUS_CODE.as_u16(), "Error '{}' test failed.", type_name ); @@ -439,8 +438,8 @@ impl TestExecutor { let invalid_builder = self.client.request(method, url); let invalid_response = T::set_invalid_request(invalid_builder).send().await?; assert_eq!( - invalid_response.status(), - T::INVALID_STATUS_CODE, + invalid_response.status().as_u16(), + T::INVALID_STATUS_CODE.as_u16(), "Invalid '{}' test failed.", type_name ); @@ -463,8 +462,8 @@ impl TestExecutor { #[cfg(feature = "into_json")] pub async fn check_json(type_name: &'static str, response: reqwest::Response) { assert_eq!( - response.headers()[axum::http::header::CONTENT_TYPE], - axum::http::HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), + response.headers()[reqwest::header::CONTENT_TYPE], + reqwest::header::HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), "'{}' rejection into json test failed", type_name ); @@ -514,9 +513,9 @@ mod typed_header { use super::{validate_again, ParametersGarde}; use crate::Garde; - use axum::headers::{Error, Header, HeaderName, HeaderValue}; use axum::http::StatusCode; - use axum::TypedHeader; + use axum_extra::headers::{Error, Header, HeaderName, HeaderValue}; + use axum_extra::typed_header::TypedHeader; pub static AXUM_VALID_PARAMETERS: HeaderName = HeaderName::from_static("axum-valid-parameters"); diff --git a/src/json.rs b/src/json.rs index 5544656..628157e 100644 --- a/src/json.rs +++ b/src/json.rs @@ -70,14 +70,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/lib.rs b/src/lib.rs index f37b8ec..6209bcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,21 +9,15 @@ pub mod form; pub mod garde; #[cfg(feature = "json")] pub mod json; -#[cfg(feature = "msgpack")] -pub mod msgpack; pub mod path; #[cfg(feature = "query")] pub mod query; #[cfg(feature = "typed_header")] pub mod typed_header; -#[cfg(feature = "typed_multipart")] -pub mod typed_multipart; #[cfg(feature = "validator")] pub mod validator; #[cfg(feature = "validify")] pub mod validify; -#[cfg(feature = "yaml")] -pub mod yaml; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; @@ -117,7 +111,8 @@ impl IntoResponse for ValidationRejection { #[cfg(test)] mod tests { - use reqwest::{RequestBuilder, StatusCode}; + use axum::http::StatusCode; + use reqwest::RequestBuilder; /// # Valid test parameter pub trait ValidTestParameter: 'static { diff --git a/src/msgpack.rs b/src/msgpack.rs deleted file mode 100644 index d9cfa16..0000000 --- a/src/msgpack.rs +++ /dev/null @@ -1,263 +0,0 @@ -//! # Support for `MsgPack` and `MsgPackRaw` from `axum-msgpack` -//! -//! ## Feature -//! -//! Enable the `msgpack` feature to use `Valid>` and `Valid>`. -//! -//! ## Usage -//! -//! 1. Implement `Deserialize` and `Validate` for your data type `T`. -//! 2. In your handler function, use `Valid>` or `Valid>` as some parameter's type. -//! -//! ## Example -//! -//! ```no_run -//! #[cfg(feature = "validator")] -//! mod validator_example { -//! use axum::routing::post; -//! use axum::Json; -//! use axum::Router; -//! use axum_msgpack::{MsgPack, MsgPackRaw}; -//! use axum_valid::Valid; -//! use serde::Deserialize; -//! use validator::Validate; -//! -//! pub fn router() -> Router { -//! Router::new() -//! .route("/msgpack", post(handler)) -//! .route("/msgpackraw", post(raw_handler)) -//! } -//! async fn handler(Valid(MsgPack(parameter)): Valid>) { -//! assert!(parameter.validate().is_ok()); -//! } -//! -//! async fn raw_handler(Valid(MsgPackRaw(parameter)): Valid>) { -//! assert!(parameter.validate().is_ok()); -//! } -//! #[derive(Validate, Deserialize)] -//! pub struct Parameter { -//! #[validate(range(min = 5, max = 10))] -//! pub v0: i32, -//! #[validate(length(min = 1, max = 10))] -//! pub v1: String, -//! } -//! } -//! -//! #[cfg(feature = "garde")] -//! mod garde_example { -//! use axum::routing::post; -//! use axum::Router; -//! use axum_msgpack::{MsgPack, MsgPackRaw}; -//! use axum_valid::Garde; -//! use serde::Deserialize; -//! use garde::Validate; -//! -//! pub fn router() -> Router { -//! Router::new() -//! .route("/msgpack", post(handler)) -//! .route("/msgpackraw", post(raw_handler)) -//! } -//! -//! async fn handler(Garde(MsgPack(parameter)): Garde>) { -//! assert!(parameter.validate(&()).is_ok()); -//! } -//! -//! async fn raw_handler(Garde(MsgPackRaw(parameter)): Garde>) { -//! assert!(parameter.validate(&()).is_ok()); -//! } -//! #[derive(Validate, Deserialize)] -//! pub struct Parameter { -//! #[garde(range(min = 5, max = 10))] -//! pub v0: i32, -//! #[garde(length(min = 1, max = 10))] -//! pub v1: String, -//! } -//! } -//! -//! # #[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; -#[cfg(feature = "validator")] -use crate::HasValidateArgs; -use axum_msgpack::{MsgPack, MsgPackRaw}; -#[cfg(feature = "validator")] -use validator::ValidateArgs; - -impl HasValidate for MsgPack { - type Validate = T; - fn get_validate(&self) -> &T { - &self.0 - } -} - -#[cfg(feature = "validator")] -impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPack { - type ValidateArgs = T; - fn get_validate_args(&self) -> &Self::ValidateArgs { - &self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::HasModify for MsgPack { - type Modify = T; - - fn get_modify(&mut self) -> &mut Self::Modify { - &mut self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::PayloadExtractor for MsgPack { - type Payload = T; - - fn get_payload(self) -> Self::Payload { - self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::HasValidify for MsgPack { - type Validify = T; - type PayloadExtractor = MsgPack; - fn from_validified(v: Self::Validify) -> Self { - MsgPack(v) - } -} - -impl HasValidate for MsgPackRaw { - type Validate = T; - fn get_validate(&self) -> &T { - &self.0 - } -} - -#[cfg(feature = "validator")] -impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPackRaw { - type ValidateArgs = T; - fn get_validate_args(&self) -> &Self::ValidateArgs { - &self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::HasModify for MsgPackRaw { - type Modify = T; - - fn get_modify(&mut self) -> &mut Self::Modify { - &mut self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::PayloadExtractor for MsgPackRaw { - type Payload = T; - - fn get_payload(self) -> Self::Payload { - self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::HasValidify for MsgPackRaw { - type Validify = T; - type PayloadExtractor = MsgPackRaw; - fn from_validified(v: Self::Validify) -> Self { - MsgPackRaw(v) - } -} - -#[cfg(test)] -mod tests { - use crate::tests::{ValidTest, ValidTestParameter}; - use axum::http::StatusCode; - use axum_msgpack::{MsgPack, MsgPackRaw}; - use reqwest::RequestBuilder; - use serde::Serialize; - - impl ValidTest for MsgPack { - const ERROR_STATUS_CODE: StatusCode = StatusCode::BAD_REQUEST; - - fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { - builder - .header(reqwest::header::CONTENT_TYPE, "application/msgpack") - .body( - rmp_serde::to_vec_named(T::valid()) - .expect("Failed to serialize parameters to msgpack"), - ) - } - - fn set_error_request(builder: RequestBuilder) -> RequestBuilder { - #[derive(Serialize, Default)] - struct ErrorData { - error_field0: i32, - error_field1: Option, - } - builder - .header(reqwest::header::CONTENT_TYPE, "application/msgpack") - .body( - rmp_serde::to_vec(&ErrorData::default()) - .expect("Failed to serialize parameters to msgpack"), - ) - } - - fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { - builder - .header(reqwest::header::CONTENT_TYPE, "application/msgpack") - .body( - rmp_serde::to_vec_named(T::invalid()) - .expect("Failed to serialize parameters to msgpack"), - ) - } - } - - impl ValidTest for MsgPackRaw { - const ERROR_STATUS_CODE: StatusCode = StatusCode::BAD_REQUEST; - - fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { - builder - .header(reqwest::header::CONTENT_TYPE, "application/msgpack") - .body( - rmp_serde::to_vec(T::valid()) - .expect("Failed to serialize parameters to msgpack"), - ) - } - - fn set_error_request(builder: RequestBuilder) -> RequestBuilder { - #[derive(Serialize, Default)] - struct ErrorData { - error_field0: i32, - error_field1: Option, - } - builder - .header(reqwest::header::CONTENT_TYPE, "application/msgpack") - .body( - rmp_serde::to_vec(&ErrorData::default()) - .expect("Failed to serialize parameters to msgpack"), - ) - } - - fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { - builder - .header(reqwest::header::CONTENT_TYPE, "application/msgpack") - .body( - rmp_serde::to_vec(T::invalid()) - .expect("Failed to serialize parameters to msgpack"), - ) - } - } -} diff --git a/src/path.rs b/src/path.rs index bc937e6..f3fedd1 100644 --- a/src/path.rs +++ b/src/path.rs @@ -66,14 +66,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/query.rs b/src/query.rs index 9e9458d..a909b69 100644 --- a/src/query.rs +++ b/src/query.rs @@ -70,14 +70,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } diff --git a/src/typed_header.rs b/src/typed_header.rs index 1802db6..1e60941 100644 --- a/src/typed_header.rs +++ b/src/typed_header.rs @@ -14,10 +14,11 @@ //! ```no_run //! #[cfg(feature = "validator")] //! mod validator_example { -//! use axum::headers::{Error, Header, HeaderValue}; +//! use axum_extra::headers::{Error, Header, HeaderValue}; +//! use axum_extra::typed_header::TypedHeader; //! use axum::http::HeaderName; //! use axum::routing::post; -//! use axum::{Router, TypedHeader}; +//! use axum::Router; //! use axum_valid::Valid; //! use validator::Validate; //! @@ -60,10 +61,11 @@ //! //! #[cfg(feature = "garde")] //! mod garde_example { -//! use axum::headers::{Error, Header, HeaderValue}; +//! use axum_extra::headers::{Error, Header, HeaderValue}; +//! use axum_extra::typed_header::TypedHeader; //! use axum::http::HeaderName; //! use axum::routing::post; -//! use axum::{Router, TypedHeader}; +//! use axum::Router; //! use axum_valid::Garde; //! use garde::Validate; //! @@ -106,14 +108,16 @@ //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { +//! # use std::net::SocketAddr; //! # use axum::Router; +//! # use tokio::net::TcpListener; //! # 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()) +//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; +//! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } @@ -122,7 +126,7 @@ use crate::HasValidate; #[cfg(feature = "validator")] use crate::HasValidateArgs; -use axum::TypedHeader; +use axum_extra::typed_header::TypedHeader; #[cfg(feature = "validator")] use validator::ValidateArgs; @@ -153,18 +157,24 @@ impl crate::HasModify for TypedHeader { #[cfg(test)] mod tests { use crate::tests::{ValidTest, ValidTestParameter}; - use axum::headers::{Header, HeaderMapExt}; use axum::http::StatusCode; - use axum::TypedHeader; - use reqwest::header::HeaderMap; + use axum_extra::headers::Header; + use axum_extra::typed_header::TypedHeader; + use reqwest::header::{HeaderMap, HeaderValue}; use reqwest::RequestBuilder; impl ValidTest for TypedHeader { const ERROR_STATUS_CODE: StatusCode = StatusCode::BAD_REQUEST; fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { + let mut vec = Vec::new(); + T::valid().encode(&mut vec); + let hv = vec.pop().unwrap(); let mut headers = HeaderMap::default(); - headers.typed_insert(T::valid().clone()); + headers.insert( + T::name().as_str(), + HeaderValue::from_bytes(hv.as_bytes()).unwrap(), + ); builder.headers(headers) } @@ -173,8 +183,14 @@ mod tests { } fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { + let mut vec = Vec::new(); + T::invalid().encode(&mut vec); + let hv = vec.pop().unwrap(); let mut headers = HeaderMap::default(); - headers.typed_insert(T::invalid().clone()); + headers.insert( + T::name().as_str(), + HeaderValue::from_bytes(hv.as_bytes()).unwrap(), + ); builder.headers(headers) } } diff --git a/src/typed_multipart.rs b/src/typed_multipart.rs deleted file mode 100644 index 55259d8..0000000 --- a/src/typed_multipart.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! # Support for `TypedMultipart` and `BaseMultipart` from `axum_typed_multipart` -//! -//! ## Feature -//! -//! Enable the `typed_multipart` feature to use `Valid>` and `Valid>`. -//! -//! ## Usage -//! -//! 1. Implement `TryFromMultipart` and `Validate` for your data type `T`. -//! 2. In your handler function, use `Valid>` or `Valid` as some parameter's type. -//! -//! ## Example -//! -//! ```no_run -//! #[cfg(feature = "validator")] -//! mod validator_example { -//! use axum::routing::post; -//! use axum::Router; -//! use axum_typed_multipart::{BaseMultipart, TryFromMultipart, TypedMultipart, TypedMultipartError}; -//! use axum_valid::Valid; -//! use validator::Validate; -//! -//! pub fn router() -> Router { -//! Router::new() -//! .route("/typed_multipart", post(handler)) -//! .route("/base_multipart", post(base_handler)) -//! } -//! -//! async fn handler(Valid(TypedMultipart(parameter)): Valid>) { -//! assert!(parameter.validate().is_ok()); -//! // Support automatic dereferencing -//! println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1); -//! } -//! -//! async fn base_handler( -//! Valid(BaseMultipart { -//! data: parameter, .. -//! }): Valid>, -//! ) { -//! assert!(parameter.validate().is_ok()); -//! } -//! -//! #[derive(TryFromMultipart, Validate)] -//! struct Parameter { -//! #[validate(range(min = 5, max = 10))] -//! v0: i32, -//! #[validate(length(min = 1, max = 10))] -//! v1: String, -//! } -//! } -//! -//! #[cfg(feature = "garde")] -//! mod garde_example { -//! use axum::routing::post; -//! use axum::Router; -//! use axum_typed_multipart::{BaseMultipart, TryFromMultipart, TypedMultipart, TypedMultipartError}; -//! use axum_valid::Garde; -//! use serde::Deserialize; -//! use garde::Validate; -//! -//! pub fn router() -> Router { -//! Router::new() -//! .route("/typed_multipart", post(handler)) -//! .route("/base_multipart", post(base_handler)) -//! } -//! -//! async fn handler(Garde(TypedMultipart(parameter)): Garde>) { -//! assert!(parameter.validate(&()).is_ok()); -//! // Support automatic dereferencing -//! println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1); -//! } -//! -//! async fn base_handler( -//! Garde(BaseMultipart { -//! data: parameter, .. -//! }): Garde>, -//! ) { -//! assert!(parameter.validate(&()).is_ok()); -//! } -//! -//! #[derive(TryFromMultipart, Validate)] -//! pub struct Parameter { -//! #[garde(range(min = 5, max = 10))] -//! pub v0: i32, -//! #[garde(length(min = 1, max = 10))] -//! pub v1: String, -//! } -//! } -//! -//! # #[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; -#[cfg(feature = "validator")] -use crate::HasValidateArgs; -use axum_typed_multipart::{BaseMultipart, TypedMultipart}; -#[cfg(feature = "validator")] -use validator::ValidateArgs; - -impl HasValidate for BaseMultipart { - type Validate = T; - fn get_validate(&self) -> &T { - &self.data - } -} - -#[cfg(feature = "validator")] -impl<'v, T: ValidateArgs<'v>, R> HasValidateArgs<'v> for BaseMultipart { - type ValidateArgs = T; - fn get_validate_args(&self) -> &Self::ValidateArgs { - &self.data - } -} - -#[cfg(feature = "validify")] -impl crate::HasModify for BaseMultipart { - type Modify = T; - - fn get_modify(&mut self) -> &mut Self::Modify { - &mut self.data - } -} - -impl HasValidate for TypedMultipart { - type Validate = T; - fn get_validate(&self) -> &T { - &self.0 - } -} - -#[cfg(feature = "validator")] -impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for TypedMultipart { - type ValidateArgs = T; - fn get_validate_args(&self) -> &Self::ValidateArgs { - &self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::HasModify for TypedMultipart { - type Modify = T; - - fn get_modify(&mut self) -> &mut Self::Modify { - &mut self.0 - } -} - -#[cfg(test)] -mod tests { - use crate::tests::{ValidTest, ValidTestParameter}; - use axum::http::StatusCode; - use axum_typed_multipart::{BaseMultipart, TypedMultipart}; - use reqwest::multipart::Form; - use reqwest::RequestBuilder; - - impl ValidTest for BaseMultipart - where - Form: From<&'static T>, - { - const ERROR_STATUS_CODE: StatusCode = StatusCode::BAD_REQUEST; - - fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { - builder.multipart(Form::from(T::valid())) - } - - fn set_error_request(builder: RequestBuilder) -> RequestBuilder { - builder.multipart(Form::new()) - } - - fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { - builder.multipart(Form::from(T::invalid())) - } - } - - impl ValidTest for TypedMultipart - where - Form: From<&'static T>, - { - const ERROR_STATUS_CODE: StatusCode = StatusCode::BAD_REQUEST; - - fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { - builder.multipart(Form::from(T::valid())) - } - - fn set_error_request(builder: RequestBuilder) -> RequestBuilder { - builder.multipart(Form::new()) - } - - fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { - builder.multipart(Form::from(T::invalid())) - } - } -} diff --git a/src/validator.rs b/src/validator.rs index 889c8e6..a02fd0b 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -10,9 +10,8 @@ pub mod test; use crate::{HasValidate, ValidationRejection}; use axum::async_trait; -use axum::extract::{FromRef, FromRequest, FromRequestParts}; +use axum::extract::{FromRef, FromRequest, FromRequestParts, Request}; use axum::http::request::Parts; -use axum::http::Request; use std::fmt::Display; use std::ops::{Deref, DerefMut}; use validator::{Validate, ValidateArgs, ValidationErrors}; @@ -155,16 +154,15 @@ pub trait HasValidateArgs<'v> { } #[async_trait] -impl FromRequest for Valid +impl FromRequest for Valid where State: Send + Sync, - Body: Send + Sync + 'static, - Extractor: HasValidate + FromRequest, + Extractor: HasValidate + FromRequest, Extractor::Validate: Validate, { - type Rejection = ValidRejection<>::Rejection>; + type Rejection = ValidRejection<>::Rejection>; - async fn from_request(req: Request, state: &State) -> Result { + async fn from_request(req: Request, state: &State) -> Result { let inner = Extractor::from_request(req, state) .await .map_err(ValidRejection::Inner)?; @@ -192,19 +190,18 @@ where } #[async_trait] -impl FromRequest for ValidEx +impl FromRequest for ValidEx where State: Send + Sync, - Body: Send + Sync + 'static, Args: Send + Sync + FromRef + for<'a> Arguments<'a, T = >::ValidateArgs>, - Extractor: for<'v> HasValidateArgs<'v> + FromRequest, + Extractor: for<'v> HasValidateArgs<'v> + FromRequest, { - type Rejection = ValidRejection<>::Rejection>; + type Rejection = ValidRejection<>::Rejection>; - async fn from_request(req: Request, state: &State) -> Result { + async fn from_request(req: Request, state: &State) -> Result { let arguments: Args = FromRef::from_ref(state); let inner = Extractor::from_request(req, state) .await diff --git a/src/validator/test.rs b/src/validator/test.rs index 855efc7..bdcb44d 100644 --- a/src/validator/test.rs +++ b/src/validator/test.rs @@ -3,24 +3,22 @@ use crate::tests::{ValidTest, ValidTestParameter}; use crate::{Arguments, HasValidate, HasValidateArgs, Valid, ValidEx, VALIDATION_ERROR_STATUS}; use axum::extract::{FromRef, Path, Query}; +use axum::http::StatusCode; use axum::routing::{get, post}; use axum::{Form, Json, Router}; use hyper::Method; use once_cell::sync::Lazy; -use reqwest::{StatusCode, Url}; +use reqwest::Url; use serde::{Deserialize, Serialize}; use std::any::type_name; use std::net::SocketAddr; use std::ops::{Deref, RangeInclusive}; use std::sync::Arc; +use tokio::net::TcpListener; use validator::{Validate, ValidateArgs, ValidationError}; #[derive(Clone, Deserialize, Serialize, Validate, Eq, PartialEq)] #[cfg_attr(feature = "extra_protobuf", derive(prost::Message))] -#[cfg_attr( - feature = "typed_multipart", - derive(axum_typed_multipart::TryFromMultipart) -)] pub struct Parameters { #[validate(range(min = 5, max = 10))] #[cfg_attr(feature = "extra_protobuf", prost(int32, tag = "1"))] @@ -32,10 +30,6 @@ pub struct Parameters { #[derive(Clone, Deserialize, Serialize, Validate, Eq, PartialEq)] #[cfg_attr(feature = "extra_protobuf", derive(prost::Message))] -#[cfg_attr( - feature = "typed_multipart", - derive(axum_typed_multipart::TryFromMultipart) -)] pub struct ParametersEx { #[validate(custom(function = "validate_v0", arg = "&'v_a RangeInclusive"))] #[cfg_attr(feature = "extra_protobuf", prost(int32, tag = "1"))] @@ -271,15 +265,14 @@ async fn test_main() -> anyhow::Result<()> { let router = router.with_state(state); - let server = axum::Server::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))) - .serve(router.into_make_service()); - let server_addr = server.local_addr(); + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + let server_addr = listener.local_addr()?; + let server = axum::serve(listener, router.into_make_service()); println!("Axum server address: {}.", server_addr); - let (server_guard, close) = tokio::sync::oneshot::channel::<()>(); - let server_handle = tokio::spawn(server.with_graceful_shutdown(async move { - let _ = close.await; - })); + tokio::spawn(async move { + let _ = server.await; + }); let server_url = format!("http://{}", server_addr); let test_executor = TestExecutor::from(Url::parse(&format!("http://{}", server_addr))?); @@ -299,8 +292,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - valid_path_response.status(), - StatusCode::OK, + valid_path_response.status().as_u16(), + StatusCode::OK.as_u16(), "Valid '{}' test failed.", path_type_name ); @@ -311,8 +304,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - error_path_response.status(), - StatusCode::BAD_REQUEST, + error_path_response.status().as_u16(), + StatusCode::BAD_REQUEST.as_u16(), "Error '{}' test failed.", path_type_name ); @@ -326,8 +319,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - invalid_path_response.status(), - VALIDATION_ERROR_STATUS, + invalid_path_response.status().as_u16(), + VALIDATION_ERROR_STATUS.as_u16(), "Invalid '{}' test failed.", path_type_name ); @@ -372,7 +365,7 @@ async fn test_main() -> anyhow::Result<()> { #[cfg(feature = "typed_header")] { - use axum::TypedHeader; + use axum_extra::typed_header::TypedHeader; // Valid test_executor .execute::>(Method::POST, typed_header::route::TYPED_HEADER) @@ -476,8 +469,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - valid_extra_typed_path_response.status(), - StatusCode::OK, + valid_extra_typed_path_response.status().as_u16(), + StatusCode::OK.as_u16(), "Valid '{}' test failed.", extra_typed_path_type_name ); @@ -488,8 +481,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - error_extra_typed_path_response.status(), - StatusCode::BAD_REQUEST, + error_extra_typed_path_response.status().as_u16(), + StatusCode::BAD_REQUEST.as_u16(), "Error '{}' test failed.", extra_typed_path_type_name ); @@ -503,8 +496,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - invalid_extra_typed_path_response.status(), - VALIDATION_ERROR_STATUS, + invalid_extra_typed_path_response.status().as_u16(), + VALIDATION_ERROR_STATUS.as_u16(), "Invalid '{}' test failed.", extra_typed_path_type_name ); @@ -583,8 +576,6 @@ async fn test_main() -> anyhow::Result<()> { .await?; } - drop(server_guard); - server_handle.await??; Ok(()) } @@ -617,8 +608,8 @@ impl TestExecutor { let valid_builder = self.client.request(method.clone(), url.clone()); let valid_response = T::set_valid_request(valid_builder).send().await?; assert_eq!( - valid_response.status(), - StatusCode::OK, + valid_response.status().as_u16(), + StatusCode::OK.as_u16(), "Valid '{}' test failed.", type_name ); @@ -626,8 +617,8 @@ impl TestExecutor { let error_builder = self.client.request(method.clone(), url.clone()); let error_response = T::set_error_request(error_builder).send().await?; assert_eq!( - error_response.status(), - T::ERROR_STATUS_CODE, + error_response.status().as_u16(), + T::ERROR_STATUS_CODE.as_u16(), "Error '{}' test failed.", type_name ); @@ -635,8 +626,8 @@ impl TestExecutor { let invalid_builder = self.client.request(method, url); let invalid_response = T::set_invalid_request(invalid_builder).send().await?; assert_eq!( - invalid_response.status(), - T::INVALID_STATUS_CODE, + invalid_response.status().as_u16(), + T::INVALID_STATUS_CODE.as_u16(), "Invalid '{}' test failed.", type_name ); @@ -659,8 +650,8 @@ impl TestExecutor { #[cfg(feature = "into_json")] pub async fn check_json(type_name: &'static str, response: reqwest::Response) { assert_eq!( - response.headers()[axum::http::header::CONTENT_TYPE], - axum::http::HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), + response.headers()[reqwest::header::CONTENT_TYPE], + reqwest::header::HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), "'{}' rejection into json test failed", type_name ); @@ -754,9 +745,9 @@ mod typed_header { use super::{validate_again, Parameters}; use super::{validate_again_ex, ParametersEx, ParametersExValidationArguments}; use crate::{Arguments, Valid, ValidEx}; - use axum::headers::{Error, Header, HeaderName, HeaderValue}; use axum::http::StatusCode; - use axum::TypedHeader; + use axum_extra::headers::{Error, Header, HeaderName, HeaderValue}; + use axum_extra::typed_header::TypedHeader; pub static AXUM_VALID_PARAMETERS: HeaderName = HeaderName::from_static("axum-valid-parameters"); diff --git a/src/validify.rs b/src/validify.rs index ac91956..9d7cb29 100644 --- a/src/validify.rs +++ b/src/validify.rs @@ -10,9 +10,8 @@ pub mod test; use crate::{HasValidate, ValidationRejection}; use axum::async_trait; -use axum::extract::{FromRequest, FromRequestParts}; +use axum::extract::{FromRequest, FromRequestParts, Request}; use axum::http::request::Parts; -use axum::http::Request; use axum::response::{IntoResponse, Response}; use std::fmt::{Display, Formatter}; use std::ops::{Deref, DerefMut}; @@ -241,16 +240,15 @@ pub trait HasValidify: Sized { } #[async_trait] -impl FromRequest for Validated +impl FromRequest for Validated where State: Send + Sync, - Body: Send + Sync + 'static, - Extractor: HasValidate + FromRequest, + Extractor: HasValidate + FromRequest, Extractor::Validate: validify::Validate, { - type Rejection = ValidifyRejection<>::Rejection>; + type Rejection = ValidifyRejection<>::Rejection>; - async fn from_request(req: Request, state: &State) -> Result { + async fn from_request(req: Request, state: &State) -> Result { let inner = Extractor::from_request(req, state) .await .map_err(ValidifyRejection::Inner)?; @@ -278,15 +276,14 @@ where } #[async_trait] -impl FromRequest for Modified +impl FromRequest for Modified where State: Send + Sync, - Body: Send + Sync + 'static, - Extractor: HasModify + FromRequest, + Extractor: HasModify + FromRequest, { - type Rejection = >::Rejection; + type Rejection = >::Rejection; - async fn from_request(req: Request, state: &State) -> Result { + async fn from_request(req: Request, state: &State) -> Result { let mut inner = Extractor::from_request(req, state).await?; inner.get_modify().modify(); Ok(Modified(inner)) @@ -309,18 +306,17 @@ where } #[async_trait] -impl FromRequest for Validified +impl FromRequest for Validified where State: Send + Sync, - Body: Send + Sync + 'static, Extractor: HasValidify, Extractor::Validify: Validify, - Extractor::PayloadExtractor: FromRequest, + Extractor::PayloadExtractor: FromRequest, { type Rejection = - ValidifyRejection<>::Rejection>; + ValidifyRejection<>::Rejection>; - async fn from_request(req: Request, state: &State) -> Result { + async fn from_request(req: Request, state: &State) -> Result { let payload = Extractor::PayloadExtractor::from_request(req, state) .await .map_err(ValidifyRejection::Inner)?; @@ -352,16 +348,15 @@ where } #[async_trait] -impl FromRequest for ValidifiedByRef +impl FromRequest for ValidifiedByRef where State: Send + Sync, - Body: Send + Sync + 'static, - Extractor: HasValidate + HasModify + FromRequest, + Extractor: HasValidate + HasModify + FromRequest, Extractor::Validate: Validate, { - type Rejection = ValidifyRejection<>::Rejection>; + type Rejection = ValidifyRejection<>::Rejection>; - async fn from_request(req: Request, state: &State) -> Result { + async fn from_request(req: Request, state: &State) -> Result { let mut inner = Extractor::from_request(req, state) .await .map_err(ValidifyRejection::Inner)?; diff --git a/src/validify/test.rs b/src/validify/test.rs index e4194dc..034cf78 100644 --- a/src/validify/test.rs +++ b/src/validify/test.rs @@ -5,15 +5,17 @@ use crate::{ HasValidate, Modified, Validated, Validified, ValidifiedByRef, VALIDATION_ERROR_STATUS, }; use axum::extract::{Path, Query}; +use axum::http::StatusCode; use axum::routing::{get, post}; use axum::{Form, Json, Router}; use hyper::Method; use once_cell::sync::Lazy; -use reqwest::{StatusCode, Url}; +use reqwest::Url; use serde::{Deserialize, Serialize}; use std::any::type_name; use std::net::SocketAddr; use std::ops::Deref; +use tokio::net::TcpListener; use validify::{Modify, Validate, Validify}; #[derive(Clone, Deserialize, Serialize, Validify, Eq, PartialEq)] @@ -300,15 +302,14 @@ async fn test_main() -> anyhow::Result<()> { post(msgpack::extract_msgpack_raw_validified_by_ref), ); - let server = axum::Server::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))) - .serve(router.into_make_service()); - let server_addr = server.local_addr(); + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + let server_addr = listener.local_addr()?; + let server = axum::serve(listener, router.into_make_service()); println!("Axum server address: {}.", server_addr); - let (server_guard, close) = tokio::sync::oneshot::channel::<()>(); - let server_handle = tokio::spawn(server.with_graceful_shutdown(async move { - let _ = close.await; - })); + tokio::spawn(async move { + let _ = server.await; + }); let server_url = format!("http://{}", server_addr); let test_executor = TestExecutor::from(Url::parse(&format!("http://{}", server_addr))?); @@ -383,8 +384,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - valid_path_response.status(), - expected_valid_status, + valid_path_response.status().as_u16(), + expected_valid_status.as_u16(), "Valid '{}' test failed.", path_type_name ); @@ -395,8 +396,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - error_path_response.status(), - expected_error_status, + error_path_response.status().as_u16(), + expected_error_status.as_u16(), "Error '{}' test failed: {}", path_type_name, error_path_response.text().await? @@ -411,8 +412,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - invalid_path_response.status(), - expected_invalid_status, + invalid_path_response.status().as_u16(), + expected_invalid_status.as_u16(), "Invalid '{}' test failed.", path_type_name ); @@ -482,7 +483,7 @@ async fn test_main() -> anyhow::Result<()> { #[cfg(feature = "typed_header")] { - use axum::TypedHeader; + use axum_extra::typed_header::TypedHeader; // Validated test_executor .execute::>( @@ -683,8 +684,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - valid_extra_typed_path_response.status(), - expected_valid_status, + valid_extra_typed_path_response.status().as_u16(), + expected_valid_status.as_u16(), "Validified '{}' test failed.", extra_typed_path_type_name ); @@ -695,8 +696,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - error_extra_typed_path_response.status(), - expected_error_status, + error_extra_typed_path_response.status().as_u16(), + expected_error_status.as_u16(), "Error '{}' test failed.", extra_typed_path_type_name ); @@ -710,8 +711,8 @@ async fn test_main() -> anyhow::Result<()> { .send() .await?; assert_eq!( - invalid_extra_typed_path_response.status(), - expected_invalid_status, + invalid_extra_typed_path_response.status().as_u16(), + expected_invalid_status.as_u16(), "Invalid '{}' test failed.", extra_typed_path_type_name ); @@ -906,8 +907,6 @@ async fn test_main() -> anyhow::Result<()> { .await?; } - drop(server_guard); - server_handle.await??; Ok(()) } @@ -994,8 +993,8 @@ impl TestExecutor { let valid_builder = self.client.request(method.clone(), url.clone()); let valid_response = T::set_valid_request(valid_builder).send().await?; assert_eq!( - valid_response.status(), - expected_valid_status, + valid_response.status().as_u16(), + expected_valid_status.as_u16(), "Validified '{}' test failed: {}.", type_name, valid_response.text().await? @@ -1004,8 +1003,8 @@ impl TestExecutor { let error_builder = self.client.request(method.clone(), url.clone()); let error_response = T::set_error_request(error_builder).send().await?; assert_eq!( - error_response.status(), - expected_error_status, + error_response.status().as_u16(), + expected_error_status.as_u16(), "Error '{}' test failed: {}.", type_name, error_response.text().await? @@ -1014,8 +1013,8 @@ impl TestExecutor { let invalid_builder = self.client.request(method, url); let invalid_response = T::set_invalid_request(invalid_builder).send().await?; assert_eq!( - invalid_response.status(), - expected_invalid_status, + invalid_response.status().as_u16(), + expected_invalid_status.as_u16(), "Invalid '{}' test failed: {}.", type_name, invalid_response.text().await? @@ -1040,8 +1039,8 @@ impl TestExecutor { #[cfg(feature = "into_json")] pub async fn check_json(type_name: &'static str, response: reqwest::Response) { assert_eq!( - response.headers()[axum::http::header::CONTENT_TYPE], - axum::http::HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), + response.headers()[reqwest::header::CONTENT_TYPE], + reqwest::header::HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()), "'{}' rejection into json test failed", type_name ); @@ -1206,9 +1205,9 @@ mod typed_header { use super::{check_modified, check_validated, check_validified, ParametersValidify}; use crate::{Modified, Validated, ValidifiedByRef}; - use axum::headers::{Error, Header, HeaderName, HeaderValue}; use axum::http::StatusCode; - use axum::TypedHeader; + use axum_extra::headers::{Error, Header, HeaderName, HeaderValue}; + use axum_extra::typed_header::TypedHeader; pub static AXUM_VALID_PARAMETERS: HeaderName = HeaderName::from_static("axum-valid-parameters"); diff --git a/src/yaml.rs b/src/yaml.rs deleted file mode 100644 index d3a079c..0000000 --- a/src/yaml.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! # Support for `Yaml` from `axum-yaml` -//! -//! ## Feature -//! -//! Enable the `yaml` feature to use `Valid>`. -//! -//! ## Usage -//! -//! 1. Implement `Deserialize` and `Validate` for your data type `T`. -//! 2. In your handler function, use `Valid>` as some parameter's type. -//! -//! ## Example -//! -//! ```no_run -//! #[cfg(feature = "validator")] -//! mod validator_example { -//! use axum::routing::post; -//! use axum_yaml::Yaml; -//! use axum::Router; -//! use axum_valid::Valid; -//! use serde::Deserialize; -//! use validator::Validate; -//! -//! pub fn router() -> Router { -//! Router::new().route("/yaml", post(handler)) -//! } -//! -//! async fn handler(Valid(Yaml(parameter)): Valid>) { -//! assert!(parameter.validate().is_ok()); -//! // Support automatic dereferencing -//! println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1); -//! } -//! -//! #[derive(Validate, Deserialize)] -//! pub struct Parameter { -//! #[validate(range(min = 5, max = 10))] -//! pub v0: i32, -//! #[validate(length(min = 1, max = 10))] -//! pub v1: String, -//! } -//! } -//! -//! #[cfg(feature = "garde")] -//! mod garde_example { -//! use axum::routing::post; -//! use axum_yaml::Yaml; -//! use axum::Router; -//! use axum_valid::Garde; -//! use serde::Deserialize; -//! use garde::Validate; -//! -//! pub fn router() -> Router { -//! Router::new().route("/yaml", post(handler)) -//! } -//! -//! async fn handler(Garde(Yaml(parameter)): Garde>) { -//! assert!(parameter.validate(&()).is_ok()); -//! // Support automatic dereferencing -//! println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1); -//! } -//! -//! #[derive(Validate, Deserialize)] -//! pub struct Parameter { -//! #[garde(range(min = 5, max = 10))] -//! pub v0: i32, -//! #[garde(length(min = 1, max = 10))] -//! pub v1: String, -//! } -//! } -//! -//! # #[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; -#[cfg(feature = "validator")] -use crate::HasValidateArgs; -use axum_yaml::Yaml; -#[cfg(feature = "validator")] -use validator::ValidateArgs; - -impl HasValidate for Yaml { - type Validate = T; - fn get_validate(&self) -> &T { - &self.0 - } -} - -#[cfg(feature = "validator")] -impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Yaml { - type ValidateArgs = T; - fn get_validate_args(&self) -> &Self::ValidateArgs { - &self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::HasModify for Yaml { - type Modify = T; - - fn get_modify(&mut self) -> &mut Self::Modify { - &mut self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::PayloadExtractor for Yaml { - type Payload = T; - - fn get_payload(self) -> Self::Payload { - self.0 - } -} - -#[cfg(feature = "validify")] -impl crate::HasValidify for Yaml { - type Validify = T; - type PayloadExtractor = Yaml; - fn from_validified(v: Self::Validify) -> Self { - Yaml(v) - } -} -#[cfg(test)] -mod tests { - use crate::tests::{ValidTest, ValidTestParameter}; - use axum::http::StatusCode; - use axum_yaml::Yaml; - use reqwest::RequestBuilder; - use serde::Serialize; - - impl ValidTest for Yaml { - const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY; - - fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { - builder - .header(reqwest::header::CONTENT_TYPE, "application/yaml") - .body( - serde_yaml::to_string(&T::valid()) - .expect("Failed to serialize parameters to yaml"), - ) - } - - fn set_error_request(builder: RequestBuilder) -> RequestBuilder { - #[derive(Serialize, Default)] - struct ErrorData { - error_field: i32, - } - builder - .header(reqwest::header::CONTENT_TYPE, "application/yaml") - .body( - serde_yaml::to_string(&ErrorData::default()) - .expect("Failed to serialize parameters to yaml"), - ) - } - - fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { - builder - .header(reqwest::header::CONTENT_TYPE, "application/yaml") - .body( - serde_yaml::to_string(&T::invalid()) - .expect("Failed to serialize parameters to yaml"), - ) - } - } -} diff --git a/tests/custom.rs b/tests/custom.rs index 106fc42..5fc8d61 100644 --- a/tests/custom.rs +++ b/tests/custom.rs @@ -4,14 +4,14 @@ #![cfg(feature = "validator")] use axum::extract::FromRequestParts; -use axum::http::request::Parts; +use axum::http::{request::Parts, StatusCode}; use axum::response::{IntoResponse, Response}; use axum::routing::get; use axum::Router; use axum_valid::{HasValidate, Valid, VALIDATION_ERROR_STATUS}; -use hyper::StatusCode; use serde::{Deserialize, Serialize}; use std::net::SocketAddr; +use tokio::net::TcpListener; use validator::Validate; const MY_DATA_HEADER: &str = "My-Data"; @@ -75,16 +75,15 @@ impl HasValidate for MyData { async fn main() -> anyhow::Result<()> { let router = Router::new().route("/", get(handler)); - let server = axum::Server::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))) - .serve(router.into_make_service()); - - let server_addr = server.local_addr(); + let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; + let server_addr = listener.local_addr()?; + let server = axum::serve(listener, router.into_make_service()); println!("Axum server address: {}.", server_addr); - let (server_guard, close) = tokio::sync::oneshot::channel::<()>(); - let server_handle = tokio::spawn(server.with_graceful_shutdown(async move { - let _ = close.await; - })); + // let (server_guard, close) = tokio::sync::oneshot::channel::<()>(); + tokio::spawn(async move { + let _ = server.await; + }); let client = reqwest::Client::default(); let url = format!("http://{}/", server_addr); @@ -97,7 +96,10 @@ async fn main() -> anyhow::Result<()> { .header(MY_DATA_HEADER, serde_json::to_string(&valid_my_data)?) .send() .await?; - assert_eq!(valid_my_data_response.status(), StatusCode::OK); + assert_eq!( + valid_my_data_response.status().as_u16(), + StatusCode::OK.as_u16() + ); let invalid_json = String::from("{{}"); let valid_my_data_response = client @@ -105,7 +107,10 @@ async fn main() -> anyhow::Result<()> { .header(MY_DATA_HEADER, invalid_json) .send() .await?; - assert_eq!(valid_my_data_response.status(), StatusCode::BAD_REQUEST); + assert_eq!( + valid_my_data_response.status().as_u16(), + StatusCode::BAD_REQUEST.as_u16() + ); let invalid_my_data = MyData { content: String::new(), @@ -115,13 +120,16 @@ async fn main() -> anyhow::Result<()> { .header(MY_DATA_HEADER, serde_json::to_string(&invalid_my_data)?) .send() .await?; - assert_eq!(invalid_my_data_response.status(), VALIDATION_ERROR_STATUS); + assert_eq!( + invalid_my_data_response.status().as_u16(), + VALIDATION_ERROR_STATUS.as_u16() + ); // #[cfg(feature = "into_json")] // test::check_json(invalid_my_data_response).await; println!("Valid works."); - drop(server_guard); - server_handle.await??; + // drop(server_guard); + // server_handle.await??; Ok(()) }