Skip to main content

modo_upload_macros/
lib.rs

1use proc_macro::TokenStream;
2
3mod from_multipart;
4
5/// Derive macro for parsing `multipart/form-data` into a struct.
6///
7/// Can only be derived for structs with named fields. Generates an implementation
8/// of `modo_upload::FromMultipart`, which is used by the `MultipartForm` extractor.
9///
10/// # Supported field types
11///
12/// | Rust type               | Behaviour                                      |
13/// |-------------------------|------------------------------------------------|
14/// | `UploadedFile`          | Required file field; errors if absent          |
15/// | `Option<UploadedFile>`  | Optional file field                            |
16/// | `Vec<UploadedFile>`     | Zero or more files under the same field name   |
17/// | `BufferedUpload`        | Required buffered upload (only one per struct) |
18/// | `String`                | Required text field                            |
19/// | `Option<String>`        | Optional text field                            |
20/// | any `T: FromStr`        | Required text field, parsed via `FromStr`      |
21///
22/// # Field attributes
23///
24/// ## `#[upload(...)]`
25///
26/// Controls validation applied to file fields. All sub-attributes are optional.
27///
28/// - `max_size = "<size>"` — maximum file size, e.g. `"5mb"`, `"100kb"`, `"2gb"`.
29///   Size strings are case-insensitive and accept the suffixes `b`, `kb`, `mb`, `gb`.
30///   A plain integer is treated as bytes.
31/// - `accept = "<pattern>"` — MIME type pattern, e.g. `"image/*"`, `"application/pdf"`.
32/// - `min_count = <n>` — minimum number of files for `Vec<UploadedFile>` fields.
33/// - `max_count = <n>` — maximum number of files for `Vec<UploadedFile>` fields.
34///
35/// ## `#[serde(rename = "...")]`
36///
37/// Overrides the multipart field name used for matching. By default the Rust field name
38/// is used as the multipart field name.
39///
40/// # Example
41///
42/// ```rust,ignore
43/// use modo_upload::{FromMultipart, UploadedFile};
44///
45/// #[derive(FromMultipart)]
46/// struct ProfileForm {
47///     #[upload(max_size = "5mb", accept = "image/*")]
48///     avatar: UploadedFile,
49///
50///     name: String,
51///
52///     #[upload(min_count = 1, max_count = 5)]
53///     attachments: Vec<UploadedFile>,
54///
55///     #[serde(rename = "user_email")]
56///     email: Option<String>,
57/// }
58/// ```
59#[proc_macro_derive(FromMultipart, attributes(upload, serde))]
60pub fn derive_from_multipart(input: TokenStream) -> TokenStream {
61    from_multipart::expand(input.into())
62        .unwrap_or_else(|e| e.to_compile_error())
63        .into()
64}