actix_multipart_derive/
lib.rs1#![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_cfg))]
10#![allow(clippy::disallowed_names)] use std::collections::HashSet;
13
14use bytesize::ByteSize;
15use darling::{FromDeriveInput, FromField, FromMeta};
16use proc_macro::TokenStream;
17use proc_macro2::Ident;
18use quote::quote;
19use syn::{parse_macro_input, Type};
20
21#[derive(Default, FromMeta)]
22enum DuplicateField {
23 #[default]
24 Ignore,
25 Deny,
26 Replace,
27}
28
29#[derive(FromDeriveInput, Default)]
30#[darling(attributes(multipart), default)]
31struct MultipartFormAttrs {
32 deny_unknown_fields: bool,
33 duplicate_field: DuplicateField,
34}
35
36#[allow(clippy::disallowed_names)] #[derive(FromField, Default)]
38#[darling(attributes(multipart), default)]
39struct FieldAttrs {
40 rename: Option<String>,
41 limit: Option<String>,
42}
43
44struct ParsedField<'t> {
45 serialization_name: String,
46 rust_name: &'t Ident,
47 limit: Option<usize>,
48 ty: &'t Type,
49}
50
51#[proc_macro_derive(MultipartForm, attributes(multipart))]
58pub fn impl_multipart_form(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
59 let input: syn::DeriveInput = parse_macro_input!(input);
60
61 let name = &input.ident;
62
63 let data_struct = match &input.data {
64 syn::Data::Struct(data_struct) => data_struct,
65 _ => {
66 return compile_err(syn::Error::new(
67 input.ident.span(),
68 "`MultipartForm` can only be derived for structs",
69 ))
70 }
71 };
72
73 let fields = match &data_struct.fields {
74 syn::Fields::Named(fields_named) => fields_named,
75 _ => {
76 return compile_err(syn::Error::new(
77 input.ident.span(),
78 "`MultipartForm` can only be derived for a struct with named fields",
79 ))
80 }
81 };
82
83 let attrs = match MultipartFormAttrs::from_derive_input(&input) {
84 Ok(attrs) => attrs,
85 Err(err) => return err.write_errors().into(),
86 };
87
88 let parsed = match fields
90 .named
91 .iter()
92 .map(|field| {
93 let rust_name = field.ident.as_ref().unwrap();
94 let attrs = FieldAttrs::from_field(field).map_err(|err| err.write_errors())?;
95 let serialization_name = attrs.rename.unwrap_or_else(|| rust_name.to_string());
96
97 let limit = match attrs.limit.map(|limit| match limit.parse::<ByteSize>() {
98 Ok(ByteSize(size)) => Ok(usize::try_from(size).unwrap()),
99 Err(err) => Err(syn::Error::new(
100 field.ident.as_ref().unwrap().span(),
101 format!("Could not parse size limit `{}`: {}", limit, err),
102 )),
103 }) {
104 Some(Err(err)) => return Err(compile_err(err)),
105 limit => limit.map(Result::unwrap),
106 };
107
108 Ok(ParsedField {
109 serialization_name,
110 rust_name,
111 limit,
112 ty: &field.ty,
113 })
114 })
115 .collect::<Result<Vec<_>, TokenStream>>()
116 {
117 Ok(attrs) => attrs,
118 Err(err) => return err,
119 };
120
121 let mut set = HashSet::new();
123 for field in &parsed {
124 if !set.insert(field.serialization_name.clone()) {
125 return compile_err(syn::Error::new(
126 field.rust_name.span(),
127 format!("Multiple fields named: `{}`", field.serialization_name),
128 ));
129 }
130 }
131
132 let unknown_field_result = if attrs.deny_unknown_fields {
134 quote!(::std::result::Result::Err(
135 ::actix_multipart::MultipartError::UnknownField(field.name().unwrap().to_string())
136 ))
137 } else {
138 quote!(::actix_multipart::form::discard_field(field, limits).await)
139 };
140
141 let duplicate_field = match attrs.duplicate_field {
143 DuplicateField::Ignore => quote!(::actix_multipart::form::DuplicateField::Ignore),
144 DuplicateField::Deny => quote!(::actix_multipart::form::DuplicateField::Deny),
145 DuplicateField::Replace => quote!(::actix_multipart::form::DuplicateField::Replace),
146 };
147
148 let mut limit_impl = quote!();
150 for field in &parsed {
151 let name = &field.serialization_name;
152 if let Some(value) = field.limit {
153 limit_impl.extend(quote!(
154 #name => ::std::option::Option::Some(#value),
155 ));
156 }
157 }
158
159 let mut handle_field_impl = quote!();
161 for field in &parsed {
162 let name = &field.serialization_name;
163 let ty = &field.ty;
164
165 handle_field_impl.extend(quote!(
166 #name => ::std::boxed::Box::pin(
167 <#ty as ::actix_multipart::form::FieldGroupReader>::handle_field(req, field, limits, state, #duplicate_field)
168 ),
169 ));
170 }
171
172 let mut from_state_impl = quote!();
174 for field in &parsed {
175 let name = &field.serialization_name;
176 let rust_name = &field.rust_name;
177 let ty = &field.ty;
178 from_state_impl.extend(quote!(
179 #rust_name: <#ty as ::actix_multipart::form::FieldGroupReader>::from_state(#name, &mut state)?,
180 ));
181 }
182
183 let gen = quote! {
184 impl ::actix_multipart::form::MultipartCollect for #name {
185 fn limit(field_name: &str) -> ::std::option::Option<usize> {
186 match field_name {
187 #limit_impl
188 _ => None,
189 }
190 }
191
192 fn handle_field<'t>(
193 req: &'t ::actix_web::HttpRequest,
194 field: ::actix_multipart::Field,
195 limits: &'t mut ::actix_multipart::form::Limits,
196 state: &'t mut ::actix_multipart::form::State,
197 ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<(), ::actix_multipart::MultipartError>> + 't>> {
198 match field.name().unwrap() {
199 #handle_field_impl
200 _ => return ::std::boxed::Box::pin(async move { #unknown_field_result }),
201 }
202 }
203
204 fn from_state(mut state: ::actix_multipart::form::State) -> ::std::result::Result<Self, ::actix_multipart::MultipartError> {
205 Ok(Self {
206 #from_state_impl
207 })
208 }
209
210 }
211 };
212 gen.into()
213}
214
215fn compile_err(err: syn::Error) -> TokenStream {
217 TokenStream::from(err.to_compile_error())
218}