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}