actix_multipart_derive/
lib.rs1#![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#[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 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 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 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 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 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 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 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
312fn compile_err(err: syn::Error) -> TokenStream {
314 TokenStream::from(err.to_compile_error())
315}