From 1b46256c39a2d1d0c1cf2312868a7bef9f3091ee Mon Sep 17 00:00:00 2001 From: gengteng Date: Mon, 4 Sep 2023 23:24:26 +0800 Subject: [PATCH] add support for TypedMultipart --- .gitignore | 1 + Cargo.toml | 16 +++++++++----- README.md | 1 + build_rs_cov.profraw | Bin 0 -> 7720 bytes src/lib.rs | 2 ++ src/test.rs | 47 +++++++++++++++++++++++++++++++++++++++++ src/typed_multipart.rs | 41 +++++++++++++++++++++++++++++++++++ 7 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 build_rs_cov.profraw create mode 100644 src/typed_multipart.rs diff --git a/.gitignore b/.gitignore index b8705c1..282dd71 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /Cargo.lock /.idea /lcov.info +/build_rs_cov.profraw diff --git a/Cargo.toml b/Cargo.toml index 6e83f65..be5923e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "axum-valid" -version = "0.5.1" -description = "Validation tools for axum using the validator library." +version = "0.6.0" +description = "Provide validator extractor for your axum application." authors = ["GengTeng "] license = "MIT" homepage = "https://github.com/gengteng/axum-valid" @@ -9,7 +9,7 @@ repository = "https://github.com/gengteng/axum-valid" keywords = [ "http", "web", - "framework", + "axum", "validator", ] categories = [ @@ -23,6 +23,11 @@ edition = "2021" axum = { version = "0.6.18", default-features = false } validator = "0.16.0" +[dependencies.axum_typed_multipart] +version = "0.9.0" +default-features = false +optional = true + [dependencies.axum-msgpack] version = "0.3.0" default-features = false @@ -43,7 +48,7 @@ anyhow = "1.0.72" axum = { version = "0.6.19" } tokio = { version = "1.29.1", features = ["full"] } hyper = { version = "0.14.27", features = ["full"] } -reqwest = { version = "0.11.18", features = ["json"] } +reqwest = { version = "0.11.18", features = ["json", "multipart"] } serde = { version = "1.0.181", features = ["derive"] } validator = { version = "0.16.0", features = ["derive"] } serde_json = "1.0.104" @@ -59,6 +64,7 @@ json = ["axum/json"] form = ["axum/form"] query = ["axum/query"] typed_header = ["axum/headers"] +typed_multipart = ["axum_typed_multipart"] msgpack = ["axum-msgpack"] yaml = ["axum-yaml"] into_json = ["json"] @@ -68,5 +74,5 @@ extra_query = ["axum-extra/query"] extra_form = ["axum-extra/form"] extra_protobuf = ["axum-extra/protobuf"] all_extra_types = ["extra", "extra_query", "extra_form", "extra_protobuf"] -all_types = ["json", "form", "query", "typed_header", "msgpack", "yaml", "all_extra_types"] +all_types = ["json", "form", "query", "typed_header", "typed_multipart", "msgpack", "yaml", "all_extra_types"] full = ["all_types", "422", "into_json"] diff --git a/README.md b/README.md index 6f69bd6..516a4dd 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ When validation errors occur, the extractor will automatically return 400 with v | query | Enables support for `Query` | ✅ | ✅ | | form | Enables support for `Form` | ✅ | ✅ | | typed_header | Enables support for `TypedHeader` | ❌ | ✅ | +| typed_multipart | Enables support for `TypedMultipart` from `axum_typed_multipart` | ❌ | ✅ | | msgpack | Enables support for `MsgPack` and `MsgPackRaw` from `axum-msgpack` | ❌ | ✅ | | yaml | Enables support for `Yaml` from `axum-yaml` | ❌ | ✅ | | extra | Enables support for `Cached`, `WithRejection` from `axum-extra` | ❌ | ✅ | diff --git a/build_rs_cov.profraw b/build_rs_cov.profraw new file mode 100644 index 0000000000000000000000000000000000000000..4a1eb66e5364b0a1a4cafe7a0b32c28794db5a33 GIT binary patch literal 7720 zcmeH}e^e7!7RM2)fUZDoYoXFzbuCz}+k_B8EGtN$qAkclMG#LXLox}0Bok*Q;fJLn zbdUB21%Dj1wOA0kC&eOdx6);8Y3*?-3bqPWsjDn4x@%QxDpf&Hc78olF)xz{f9xO4 zIWY5PKKI`5+;`u5cO#*}36p-<$K&pw?Czn*=cC4(rg9H_ZVj`)gybgA3G^f8kNGtW z8P1$Nm#v&MrB8Fylp75=?ydd57k|5N>TzhL)$g|TM^BOk5^Z@-;FMs)GzqXlsnx2&Pyqa-(=mUuUiM+tsgmV9o zb!$S+ET4Ustp1f&2=C4F2^MK*O>#kI?)k`l2dw8ULjKbmYVtl^tXPofmpy;V?|)ej z^AGeFp4hL5-j?%m{+0t7&)k+3ZHDLp{9pU$6$P(qPoF;V$4E{0?@nb2@RtVC-@ldB z-*@NB&cgtA?0mfn18?*K4yM*vUJbxiuSh} zhqeSBvh_Qzf1uyIphKB|vh}3@+!NOF;;YS_5Z;&9kEae?dF1iR#ej$T2mE)n>X@)U z><;-h&u`i3A0Dpo5%}KdMaHkT&#m9P;Y3L1`r3_U7a@9p{yAMg^`!Tfw-+aE@~%l( zM;(XoAV12A!(z`@4(dZwaZUFhm7)(IJn(;KXL*pg`Eo?h9DPpqlBaCD1o$KVor%de z>R;+8erTd>`fL9xg7833;ZrwrSMinne&E=-L0wjy2sE2i7k9Pu-D*&|E42!UH`+Q=4;I zNwHmc=Mnh?bn59h&X^!P@Sl+xUDNP=ffn$|5FYFg585Q=ru9R0^*?;Ht;u@$x{&`x zOGP3xeJ?ZGUy@vsMAV{gLL~8OsavhF>^yU?sk(XtMw>eSAUk z)-w5(jA?0s-cQ?ihy!4MLwc5di3%8p+T3Z$d3h-~v^g0+- zw<4yhHZ(h_t7M@6y7PJny!vhHkpCyMFUt#}_6LgwItBQji`TY=zUB31?}XHr`J1LG z{tn?mK0#jzuKcm)0rCOHqu+z)W4*%sP8zFYh&?PGz=hreZoICefZ9R1k;AP&0+AG+LPjBS;-? zOrm1L5LqfWkQlMt2}}_8%)}xMfg2FQOj0VwT;ZC7@R7K`b|8w4Kv)fFB;ta2yOSh} z!Nqc|J}Kupy>_Vv30L6+CROQiBPLa_|Ji$c0Ff&Zq$4sR5hNM|C5^)}&06M;*B&-L zu2lw;JQ*b@9jcFC7ly5im4W0)$1Q+Z#79_8sKsIv7Y(5!+k-`nTQCV7M8f$#6u=j> zqxuZT3_32Cj_X$}A~3>iL`*1^#irabCg&*3XmJAB%iggQZjCIa7mh-s<>94tjYDY( zBID`ETpg8#sC5KWJ4}7%6eJ~^!&1}MXjO=Wq|_3PMi+sT2uTr<%gGfKp)+dZVvBv%M6pt?mf+t(?+knhvD`%9nHaam5q|GrQ|gj+Br}`$7(4wudk&>+XfAzn zbLL7?oubV*$01Twug6spbmr)sMckD-w>+m{AmL7?^xjmE`6Pkk6rv(f3UfCV_h~HR zW?`i0T}r-$O@0yA+LG@Cc*wz6@C-_jK~fr1DK} zOrJfO_hZM{xF<4#OD2|QA~2FUuw9=AIXmxmOZNhYbAeU3k)%)~w-viIkuo+cup0^L zc(aT!phm5p%|G2WIAuiqt_Y#Ta(b6V=&%&s{m?~(5?YK^4fX5L0z^?TU5sNrj_ts9 z${4z9D&kMt`>`bvU=5KgXZkXY9@Uae=|H8~F^~ca-GMpI0DU0ReHVW;K)NCaeOEZ> zT|{ud_EQbw_wH`X_5j;Mq`^8xDMk=DefZK%t~8#zb=)Pg9EXU-YK-fr!Ezf{_fi#V zCb=EQNsBuoJlwf(CLOXk9cj{|`6KP8^K8i2Ze547JBf2Nl0}VXgA1&jWzCc((gm1v nOds7Wn?}Mr@sUJGg%fd47cPeOt-o?BXA9#wol)&l$$9(_`FB`l literal 0 HcmV?d00001 diff --git a/src/lib.rs b/src/lib.rs index d2b7812..c493b0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,8 @@ pub mod query; pub mod test; #[cfg(feature = "typed_header")] pub mod typed_header; +#[cfg(feature = "typed_multipart")] +pub mod typed_multipart; #[cfg(feature = "yaml")] pub mod yaml; diff --git a/src/test.rs b/src/test.rs index 441c409..0d91218 100644 --- a/src/test.rs +++ b/src/test.rs @@ -14,6 +14,10 @@ use validator::Validate; #[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"))] @@ -69,6 +73,12 @@ async fn test_main() -> anyhow::Result<()> { post(typed_header::extract_typed_header), ); + #[cfg(feature = "typed_multipart")] + let router = router.route( + typed_multipart::route::TYPED_MULTIPART, + post(typed_multipart::extract_typed_header), + ); + #[cfg(feature = "extra")] let router = router .route(extra::route::CACHED, post(extra::extract_cached)) @@ -191,6 +201,17 @@ async fn test_main() -> anyhow::Result<()> { .await?; } + #[cfg(feature = "typed_multipart")] + { + use axum_typed_multipart::TypedMultipart; + test_executor + .execute::>( + Method::POST, + typed_multipart::route::TYPED_MULTIPART, + ) + .await?; + } + #[cfg(feature = "extra")] { use axum_extra::extract::{Cached, WithRejection}; @@ -443,6 +464,32 @@ mod typed_header { } } +#[cfg(feature = "typed_multipart")] +mod typed_multipart { + use crate::test::{validate_again, Parameters}; + use crate::Valid; + use axum::http::StatusCode; + use axum_typed_multipart::TypedMultipart; + + pub mod route { + pub const TYPED_MULTIPART: &str = "/typed_multipart"; + } + + impl From<&Parameters> for reqwest::multipart::Form { + fn from(value: &Parameters) -> Self { + reqwest::multipart::Form::new() + .text("v0", value.v0.to_string()) + .text("v1", value.v1.clone()) + } + } + + pub(super) async fn extract_typed_header( + Valid(TypedMultipart(parameters)): Valid>, + ) -> StatusCode { + validate_again(parameters) + } +} + #[cfg(feature = "extra")] mod extra { use crate::test::{validate_again, Parameters}; diff --git a/src/typed_multipart.rs b/src/typed_multipart.rs new file mode 100644 index 0000000..7d2df21 --- /dev/null +++ b/src/typed_multipart.rs @@ -0,0 +1,41 @@ +//! # Implementation of the `HasValidate` trait for the `TypedMultipart` extractor. +//! + +use crate::HasValidate; +use axum_typed_multipart::TypedMultipart; +use validator::Validate; + +impl HasValidate for TypedMultipart { + type Validate = T; + fn get_validate(&self) -> &T { + &self.0 + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{ValidTest, ValidTestParameter}; + use axum::http::StatusCode; + use axum_typed_multipart::TypedMultipart; + use reqwest::multipart::Form; + use reqwest::RequestBuilder; + + 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())) + } + } +}