From afd2df2f69dd848a8411f5c0c848329a605ae265 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 21 Jun 2026 13:51:22 +0900 Subject: [PATCH] test(multipart,derive): move trybuild tests (#4028) * test(multipart,derive): move trybuild tests * docs(multipart): move derive examples to the core crate --- Cargo.lock | 6 +- actix-multipart-derive/Cargo.toml | 6 -- actix-multipart-derive/src/lib.rs | 102 +----------------- actix-multipart/Cargo.toml | 7 ++ actix-multipart/src/form/mod.rs | 101 +++++++++++++++++ .../tests/trybuild.rs | 0 .../tests/trybuild/all-required.rs | 0 .../tests/trybuild/deny-duplicates.rs | 0 .../tests/trybuild/deny-parse-fail.rs | 0 .../tests/trybuild/deny-parse-fail.stderr | 0 .../tests/trybuild/deny-unknown.rs | 0 .../tests/trybuild/optional-and-list.rs | 0 .../tests/trybuild/rename.rs | 0 .../tests/trybuild/size-limit-parse-fail.rs | 0 .../trybuild/size-limit-parse-fail.stderr | 0 .../tests/trybuild/size-limits.rs | 0 16 files changed, 115 insertions(+), 107 deletions(-) rename {actix-multipart-derive => actix-multipart}/tests/trybuild.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/all-required.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/deny-duplicates.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/deny-parse-fail.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/deny-parse-fail.stderr (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/deny-unknown.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/optional-and-list.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/rename.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/size-limit-parse-fail.rs (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/size-limit-parse-fail.stderr (100%) rename {actix-multipart-derive => actix-multipart}/tests/trybuild/size-limits.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 60905cd27..1e39e0e4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,27 +165,25 @@ dependencies = [ "mime", "multer", "rand 0.10.1", + "rustversion-msrv", "serde", "serde_json", "serde_plain", "tempfile", "tokio", "tokio-stream", + "trybuild", ] [[package]] name = "actix-multipart-derive" version = "0.8.0" dependencies = [ - "actix-multipart", - "actix-web", "bytesize", "darling", "proc-macro2", "quote", - "rustversion-msrv", "syn", - "trybuild", ] [[package]] diff --git a/actix-multipart-derive/Cargo.toml b/actix-multipart-derive/Cargo.toml index 6d64568e6..c8ff6b901 100644 --- a/actix-multipart-derive/Cargo.toml +++ b/actix-multipart-derive/Cargo.toml @@ -23,11 +23,5 @@ proc-macro2 = "1" quote = "1" syn = "2" -[dev-dependencies] -actix-multipart = "0.8" -actix-web = "4" -rustversion-msrv = "0.100" -trybuild = "1" - [lints] workspace = true diff --git a/actix-multipart-derive/src/lib.rs b/actix-multipart-derive/src/lib.rs index 161a9ba24..06a5c92bd 100644 --- a/actix-multipart-derive/src/lib.rs +++ b/actix-multipart-derive/src/lib.rs @@ -1,6 +1,8 @@ //! Multipart form derive macro for Actix Web. //! -//! See [`macro@MultipartForm`] for usage examples. +//! See [`actix_multipart::form::MultipartForm`] for usage examples. +//! +//! [`actix_multipart::form::MultipartForm`]: https://docs.rs/actix-multipart/latest/actix_multipart/form/struct.MultipartForm.html #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] @@ -49,103 +51,9 @@ struct ParsedField<'t> { /// Implements `MultipartCollect` for a struct so that it can be used with the `MultipartForm` /// extractor. /// -/// # Basic Use +/// See [`actix_multipart::form::MultipartForm`] for supported fields and attributes. /// -/// Each field type should implement the `FieldReader` trait: -/// -/// ``` -/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; -/// -/// #[derive(MultipartForm)] -/// struct ImageUpload { -/// description: Text, -/// timestamp: Text, -/// image: TempFile, -/// } -/// ``` -/// -/// # Optional and List Fields -/// -/// You can also use `Vec` and `Option` provided that `T: FieldReader`. -/// -/// A [`Vec`] field corresponds to an upload with multiple parts under the [same field -/// name](https://www.rfc-editor.org/rfc/rfc7578#section-4.3). -/// -/// ``` -/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; -/// -/// #[derive(MultipartForm)] -/// struct Form { -/// category: Option>, -/// files: Vec, -/// } -/// ``` -/// -/// # Field Renaming -/// -/// You can use the `#[multipart(rename = "foo")]` attribute to receive a field by a different name. -/// -/// ``` -/// use actix_multipart::form::{tempfile::TempFile, MultipartForm}; -/// -/// #[derive(MultipartForm)] -/// struct Form { -/// #[multipart(rename = "files[]")] -/// files: Vec, -/// } -/// ``` -/// -/// # Field Limits -/// -/// You can use the `#[multipart(limit = "")]` attribute to set field level limits. The limit -/// string is parsed using [`bytesize`]. -/// -/// Note: the form is also subject to the global limits configured using `MultipartFormConfig`. -/// -/// ``` -/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; -/// -/// #[derive(MultipartForm)] -/// struct Form { -/// #[multipart(limit = "2 KiB")] -/// description: Text, -/// -/// #[multipart(limit = "512 MiB")] -/// files: Vec, -/// } -/// ``` -/// -/// # Unknown Fields -/// -/// By default fields with an unknown name are ignored. They can be rejected using the -/// `#[multipart(deny_unknown_fields)]` attribute: -/// -/// ``` -/// # use actix_multipart::form::MultipartForm; -/// #[derive(MultipartForm)] -/// #[multipart(deny_unknown_fields)] -/// struct Form { } -/// ``` -/// -/// # Duplicate Fields -/// -/// The behaviour for when multiple fields with the same name are received can be changed using the -/// `#[multipart(duplicate_field = "")]` attribute: -/// -/// - "ignore": (default) Extra fields are ignored. I.e., the first one is persisted. -/// - "deny": A `MultipartError::UnknownField` error response is returned. -/// - "replace": Each field is processed, but only the last one is persisted. -/// -/// Note that `Vec` fields will ignore this option. -/// -/// ``` -/// # use actix_multipart::form::MultipartForm; -/// #[derive(MultipartForm)] -/// #[multipart(duplicate_field = "deny")] -/// struct Form { } -/// ``` -/// -/// [`bytesize`]: https://docs.rs/bytesize/2 +/// [`actix_multipart::form::MultipartForm`]: https://docs.rs/actix-multipart/latest/actix_multipart/form/struct.MultipartForm.html #[proc_macro_derive(MultipartForm, attributes(multipart))] pub fn impl_multipart_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input: syn::DeriveInput = parse_macro_input!(input); diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 317abaca4..05dc21ff7 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -12,10 +12,15 @@ homepage.workspace = true repository.workspace = true license.workspace = true edition.workspace = true +rust-version.workspace = true [package.metadata.docs.rs] all-features = true +[[test]] +name = "trybuild" +required-features = ["derive", "tempfile"] + [package.metadata.cargo_check_external_types] allowed_external_types = [ "actix_http::*", @@ -68,8 +73,10 @@ env_logger = "0.11" futures-test = "0.3" futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] } multer = "3" +rustversion-msrv = "0.100" tokio = { version = "1.38.2", features = ["sync"] } tokio-stream = "0.1" +trybuild = "1" [lints] workspace = true diff --git a/actix-multipart/src/form/mod.rs b/actix-multipart/src/form/mod.rs index 51136869b..765b0a570 100644 --- a/actix-multipart/src/form/mod.rs +++ b/actix-multipart/src/form/mod.rs @@ -336,6 +336,107 @@ pub async fn discard_field(mut field: Field, limits: &mut Limits) -> Result<(), /// `multipart/related`, or non-multipart media types. /// /// Add a [`MultipartFormConfig`] to your app data to configure extraction. +/// +/// # Basic Use +/// +/// Each field type should implement the [`FieldReader`] trait: +/// +/// ```rust +/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; +/// +/// #[derive(MultipartForm)] +/// struct ImageUpload { +/// description: Text, +/// timestamp: Text, +/// image: TempFile, +/// } +/// ``` +/// +/// # Optional and List Fields +/// +/// You can also use [`Vec`](Vec) and [`Option`](Option) provided that `T: FieldReader`. +/// +/// A [`Vec`] field corresponds to an upload with multiple parts under the [same field +/// name](https://www.rfc-editor.org/rfc/rfc7578#section-4.3). +/// +/// ```rust +/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; +/// +/// #[derive(MultipartForm)] +/// struct Form { +/// category: Option>, +/// files: Vec, +/// } +/// ``` +/// +/// # Field Renaming +/// +/// You can use the `#[multipart(rename = "foo")]` attribute to receive a field by a different name. +/// +/// ```rust +/// use actix_multipart::form::{tempfile::TempFile, MultipartForm}; +/// +/// #[derive(MultipartForm)] +/// struct Form { +/// #[multipart(rename = "files[]")] +/// files: Vec, +/// } +/// ``` +/// +/// # Field Limits +/// +/// You can use the `#[multipart(limit = "")]` attribute to set field level limits. The limit +/// string is parsed using [`bytesize`]. +/// +/// Note: the form is also subject to the global limits configured using [`MultipartFormConfig`]. +/// +/// ```rust +/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; +/// +/// #[derive(MultipartForm)] +/// struct Form { +/// #[multipart(limit = "2 KiB")] +/// description: Text, +/// +/// #[multipart(limit = "512 MiB")] +/// files: Vec, +/// } +/// ``` +/// +/// # Unknown Fields +/// +/// By default fields with an unknown name are ignored. They can be rejected using the +/// `#[multipart(deny_unknown_fields)]` attribute: +/// +/// ```rust +/// use actix_multipart::form::MultipartForm; +/// +/// #[derive(MultipartForm)] +/// #[multipart(deny_unknown_fields)] +/// struct Form {} +/// ``` +/// +/// # Duplicate Fields +/// +/// The behaviour for when multiple fields with the same name are received can be changed using the +/// `#[multipart(duplicate_field = "")]` attribute: +/// +/// - "ignore": (default) Extra fields are ignored. I.e., the first one is persisted. +/// - "deny": A [`MultipartError::DuplicateField`] error response is returned. +/// - "replace": Each field is processed, but only the last one is persisted. +/// +/// Note that [`Vec`] fields will ignore this option. +/// +/// ```rust +/// use actix_multipart::form::MultipartForm; +/// +/// #[derive(MultipartForm)] +/// #[multipart(duplicate_field = "deny")] +/// struct Form {} +/// ``` +/// +/// [`bytesize`]: https://docs.rs/bytesize/2 +/// [`MultipartError::DuplicateField`]: crate::MultipartError::DuplicateField #[derive(Deref, DerefMut)] pub struct MultipartForm(pub T); diff --git a/actix-multipart-derive/tests/trybuild.rs b/actix-multipart/tests/trybuild.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild.rs rename to actix-multipart/tests/trybuild.rs diff --git a/actix-multipart-derive/tests/trybuild/all-required.rs b/actix-multipart/tests/trybuild/all-required.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/all-required.rs rename to actix-multipart/tests/trybuild/all-required.rs diff --git a/actix-multipart-derive/tests/trybuild/deny-duplicates.rs b/actix-multipart/tests/trybuild/deny-duplicates.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/deny-duplicates.rs rename to actix-multipart/tests/trybuild/deny-duplicates.rs diff --git a/actix-multipart-derive/tests/trybuild/deny-parse-fail.rs b/actix-multipart/tests/trybuild/deny-parse-fail.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/deny-parse-fail.rs rename to actix-multipart/tests/trybuild/deny-parse-fail.rs diff --git a/actix-multipart-derive/tests/trybuild/deny-parse-fail.stderr b/actix-multipart/tests/trybuild/deny-parse-fail.stderr similarity index 100% rename from actix-multipart-derive/tests/trybuild/deny-parse-fail.stderr rename to actix-multipart/tests/trybuild/deny-parse-fail.stderr diff --git a/actix-multipart-derive/tests/trybuild/deny-unknown.rs b/actix-multipart/tests/trybuild/deny-unknown.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/deny-unknown.rs rename to actix-multipart/tests/trybuild/deny-unknown.rs diff --git a/actix-multipart-derive/tests/trybuild/optional-and-list.rs b/actix-multipart/tests/trybuild/optional-and-list.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/optional-and-list.rs rename to actix-multipart/tests/trybuild/optional-and-list.rs diff --git a/actix-multipart-derive/tests/trybuild/rename.rs b/actix-multipart/tests/trybuild/rename.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/rename.rs rename to actix-multipart/tests/trybuild/rename.rs diff --git a/actix-multipart-derive/tests/trybuild/size-limit-parse-fail.rs b/actix-multipart/tests/trybuild/size-limit-parse-fail.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/size-limit-parse-fail.rs rename to actix-multipart/tests/trybuild/size-limit-parse-fail.rs diff --git a/actix-multipart-derive/tests/trybuild/size-limit-parse-fail.stderr b/actix-multipart/tests/trybuild/size-limit-parse-fail.stderr similarity index 100% rename from actix-multipart-derive/tests/trybuild/size-limit-parse-fail.stderr rename to actix-multipart/tests/trybuild/size-limit-parse-fail.stderr diff --git a/actix-multipart-derive/tests/trybuild/size-limits.rs b/actix-multipart/tests/trybuild/size-limits.rs similarity index 100% rename from actix-multipart-derive/tests/trybuild/size-limits.rs rename to actix-multipart/tests/trybuild/size-limits.rs