actix_multipart_derive/
lib.rs

1//! Multipart form derive macro for Actix Web.
2//!
3//! See [`macro@MultipartForm`] for usage examples.
4
5#![deny(rust_2018_idioms, nonstandard_style)]
6#![warn(future_incompatible)]
7#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
8#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
9#![cfg_attr(docsrs, feature(doc_auto_cfg))]
10
11use std::collections::HashSet;
12
13use darling::{FromDeriveInput, FromField, FromMeta};
14use parse_size::parse_size;
15use proc_macro::TokenStream;
16use proc_macro2::Ident;
17use quote::quote;
18use syn::{parse_macro_input, Type};
19
20#[derive(FromMeta)]
21enum DuplicateField {
22    Ignore,
23    Deny,
24    Replace,
25}
26
27impl Default for DuplicateField {
28    fn default() -> Self {
29        Self::Ignore
30    }
31}
32
33#[derive(FromDeriveInput, Default)]
34#[darling(attributes(multipart), default)]
35struct MultipartFormAttrs {
36    deny_unknown_fields: bool,
37    duplicate_field: DuplicateField,
38}
39
40#[derive(FromField, Default)]
41#[darling(attributes(multipart), default)]
42struct FieldAttrs {
43    rename: Option<String>,
44    limit: Option<String>,
45}
46
47struct ParsedField<'t> {
48    serialization_name: String,
49    rust_name: &'t Ident,
50    limit: Option<usize>,
51    ty: &'t Type,
52}
53
54/// Implements `MultipartCollect` for a struct so that it can be used with the `MultipartForm`
55/// extractor.
56///
57/// # Basic Use
58///
59/// Each field type should implement the `FieldReader` trait:
60///
61/// ```
62/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
63///
64/// #[derive(MultipartForm)]
65/// struct ImageUpload {
66///     description: Text<String>,
67///     timestamp: Text<i64>,
68///     image: TempFile,
69/// }
70/// ```
71///
72/// # Optional and List Fields
73///
74/// You can also use `Vec<T>` and `Option<T>` provided that `T: FieldReader`.
75///
76/// A [`Vec`] field corresponds to an upload with multiple parts under the [same field
77/// name](https://www.rfc-editor.org/rfc/rfc7578#section-4.3).
78///
79/// ```
80/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
81///
82/// #[derive(MultipartForm)]
83/// struct Form {
84///     category: Option<Text<String>>,
85///     files: Vec<TempFile>,
86/// }
87/// ```
88///
89/// # Field Renaming
90///
91/// You can use the `#[multipart(rename = "foo")]` attribute to receive a field by a different name.
92///
93/// ```
94/// use actix_multipart::form::{tempfile::TempFile, MultipartForm};
95///
96/// #[derive(MultipartForm)]
97/// struct Form {
98///     #[multipart(rename = "files[]")]
99///     files: Vec<TempFile>,
100/// }
101/// ```
102///
103/// # Field Limits
104///
105/// You can use the `#[multipart(limit = "<size>")]` attribute to set field level limits. The limit
106/// string is parsed using [parse_size].
107///
108/// Note: the form is also subject to the global limits configured using `MultipartFormConfig`.
109///
110/// ```
111/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
112///
113/// #[derive(MultipartForm)]
114/// struct Form {
115///     #[multipart(limit = "2 KiB")]
116///     description: Text<String>,
117///
118///     #[multipart(limit = "512 MiB")]
119///     files: Vec<TempFile>,
120/// }
121/// ```
122///
123/// # Unknown Fields
124///
125/// By default fields with an unknown name are ignored. They can be rejected using the
126/// `#[multipart(deny_unknown_fields)]` attribute:
127///
128/// ```
129/// # use actix_multipart::form::MultipartForm;
130/// #[derive(MultipartForm)]
131/// #[multipart(deny_unknown_fields)]
132/// struct Form { }
133/// ```
134///
135/// # Duplicate Fields
136///
137/// The behaviour for when multiple fields with the same name are received can be changed using the
138/// `#[multipart(duplicate_field = "<behavior>")]` attribute:
139///
140/// - "ignore": (default) Extra fields are ignored. I.e., the first one is persisted.
141/// - "deny": A `MultipartError::UnknownField` error response is returned.
142/// - "replace": Each field is processed, but only the last one is persisted.
143///
144/// Note that `Vec` fields will ignore this option.
145///
146/// ```
147/// # use actix_multipart::form::MultipartForm;
148/// #[derive(MultipartForm)]
149/// #[multipart(duplicate_field = "deny")]
150/// struct Form { }
151/// ```
152///
153/// [parse_size]: https://docs.rs/parse-size/1/parse_size
154#[proc_macro_derive(MultipartForm, attributes(multipart))]
155pub fn impl_multipart_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
156    let input: syn::DeriveInput = parse_macro_input!(input);
157
158    let name = &input.ident;
159
160    let data_struct = match &input.data {
161        syn::Data::Struct(data_struct) => data_struct,
162        _ => {
163            return compile_err(syn::Error::new(
164                input.ident.span(),
165                "`MultipartForm` can only be derived for structs",
166            ))
167        }
168    };
169
170    let fields = match &data_struct.fields {
171        syn::Fields::Named(fields_named) => fields_named,
172        _ => {
173            return compile_err(syn::Error::new(
174                input.ident.span(),
175                "`MultipartForm` can only be derived for a struct with named fields",
176            ))
177        }
178    };
179
180    let attrs = match MultipartFormAttrs::from_derive_input(&input) {
181        Ok(attrs) => attrs,
182        Err(err) => return err.write_errors().into(),
183    };
184
185    // Parse the field attributes
186    let parsed = match fields
187        .named
188        .iter()
189        .map(|field| {
190            let rust_name = field.ident.as_ref().unwrap();
191            let attrs = FieldAttrs::from_field(field).map_err(|err| err.write_errors())?;
192            let serialization_name = attrs.rename.unwrap_or_else(|| rust_name.to_string());
193
194            let limit = match attrs.limit.map(|limit| match parse_size(&limit) {
195                Ok(size) => Ok(usize::try_from(size).unwrap()),
196                Err(err) => Err(syn::Error::new(
197                    field.ident.as_ref().unwrap().span(),
198                    format!("Could not parse size limit `{}`: {}", limit, err),
199                )),
200            }) {
201                Some(Err(err)) => return Err(compile_err(err)),
202                limit => limit.map(Result::unwrap),
203            };
204
205            Ok(ParsedField {
206                serialization_name,
207                rust_name,
208                limit,
209                ty: &field.ty,
210            })
211        })
212        .collect::<Result<Vec<_>, TokenStream>>()
213    {
214        Ok(attrs) => attrs,
215        Err(err) => return err,
216    };
217
218    // Check that field names are unique
219    let mut set = HashSet::new();
220    for field in &parsed {
221        if !set.insert(field.serialization_name.clone()) {
222            return compile_err(syn::Error::new(
223                field.rust_name.span(),
224                format!("Multiple fields named: `{}`", field.serialization_name),
225            ));
226        }
227    }
228
229    // Return value when a field name is not supported by the form
230    let unknown_field_result = if attrs.deny_unknown_fields {
231        quote!(::std::result::Result::Err(
232            ::actix_multipart::MultipartError::UnknownField(field.name().unwrap().to_string())
233        ))
234    } else {
235        quote!(::std::result::Result::Ok(()))
236    };
237
238    // Value for duplicate action
239    let duplicate_field = match attrs.duplicate_field {
240        DuplicateField::Ignore => quote!(::actix_multipart::form::DuplicateField::Ignore),
241        DuplicateField::Deny => quote!(::actix_multipart::form::DuplicateField::Deny),
242        DuplicateField::Replace => quote!(::actix_multipart::form::DuplicateField::Replace),
243    };
244
245    // limit() implementation
246    let mut limit_impl = quote!();
247    for field in &parsed {
248        let name = &field.serialization_name;
249        if let Some(value) = field.limit {
250            limit_impl.extend(quote!(
251                #name => ::std::option::Option::Some(#value),
252            ));
253        }
254    }
255
256    // handle_field() implementation
257    let mut handle_field_impl = quote!();
258    for field in &parsed {
259        let name = &field.serialization_name;
260        let ty = &field.ty;
261
262        handle_field_impl.extend(quote!(
263            #name => ::std::boxed::Box::pin(
264                <#ty as ::actix_multipart::form::FieldGroupReader>::handle_field(req, field, limits, state, #duplicate_field)
265            ),
266        ));
267    }
268
269    // from_state() implementation
270    let mut from_state_impl = quote!();
271    for field in &parsed {
272        let name = &field.serialization_name;
273        let rust_name = &field.rust_name;
274        let ty = &field.ty;
275        from_state_impl.extend(quote!(
276            #rust_name: <#ty as ::actix_multipart::form::FieldGroupReader>::from_state(#name, &mut state)?,
277        ));
278    }
279
280    let gen = quote! {
281        impl ::actix_multipart::form::MultipartCollect for #name {
282            fn limit(field_name: &str) -> ::std::option::Option<usize> {
283                match field_name {
284                    #limit_impl
285                    _ => None,
286                }
287            }
288
289            fn handle_field<'t>(
290                req: &'t ::actix_web::HttpRequest,
291                field: ::actix_multipart::Field,
292                limits: &'t mut ::actix_multipart::form::Limits,
293                state: &'t mut ::actix_multipart::form::State,
294            ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<(), ::actix_multipart::MultipartError>> + 't>> {
295                match field.name().unwrap() {
296                    #handle_field_impl
297                    _ => return ::std::boxed::Box::pin(::std::future::ready(#unknown_field_result)),
298                }
299            }
300
301            fn from_state(mut state: ::actix_multipart::form::State) -> ::std::result::Result<Self, ::actix_multipart::MultipartError> {
302                Ok(Self {
303                    #from_state_impl
304                })
305            }
306
307        }
308    };
309    gen.into()
310}
311
312/// Transform a syn error into a token stream for returning.
313fn compile_err(err: syn::Error) -> TokenStream {
314    TokenStream::from(err.to_compile_error())
315}