actix_multipart_extract_derive/
lib.rs1use parse_size::parse_size;
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use quote::quote;
5use syn::{
6 parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed, Ident, Lit, Meta, MetaList,
7 MetaNameValue, NestedMeta, Path,
8};
9
10#[proc_macro_derive(MultipartForm, attributes(multipart))]
11pub fn multipart_form(input: TokenStream) -> TokenStream {
12 let ast = parse_macro_input!(input as DeriveInput);
13 let name = ast.ident;
14
15 let fields = if let Data::Struct(syn::DataStruct {
16 fields: Fields::Named(FieldsNamed { ref named, .. }),
17 ..
18 }) = ast.data
19 {
20 named
21 } else {
22 panic!("can only derive on a struct")
23 };
24
25 let field_max_sizes = fields.iter().map(|field| {
26 let Field { attrs, .. } = field;
27
28 for attr in attrs {
29 if let Ok(meta) = attr.parse_meta() {
30 if let Meta::List(MetaList { path, nested, .. }) = meta {
31 if path.get_ident().unwrap()
33 != &Ident::new("multipart", proc_macro2::Span::call_site())
34 {
35 continue;
36 }
37
38 if let Some(NestedMeta::Meta(Meta::NameValue(MetaNameValue {
39 path: Path { segments, .. },
40 lit,
41 ..
42 }))) = nested.first()
43 {
44 for segment in segments {
45 if &segment.ident == &Ident::new("max_size", Span::call_site()) {
46 let lit_string = match lit {
47 Lit::Int(l) => l.to_string(),
48 Lit::Float(f) => f.to_string(),
49 _ => {
50 return syn::Error::new(
51 lit.span(),
52 "must be a number with size suffix",
53 )
54 .to_compile_error()
55 }
56 };
57
58 let max_size = match parse_size(lit_string) {
59 Ok(v) => v as usize,
60 Err(_) => {
61 return syn::Error::new(lit.span(), "invalid size")
62 .to_compile_error();
63 }
64 };
65
66 return quote! { Some(#max_size) };
67 }
68 }
69 }
70 }
71 }
72 }
73
74 quote! { None }
75 });
76
77 let field_len = field_max_sizes.len();
78
79 let expanded = quote! {
80 impl actix_multipart_extract::form::MultipartForm for #name {
81 fn max_size(field: &str) -> Option<usize> {
82 static max_sizes: [Option<usize>; #field_len] = [#(#field_max_sizes,)*];
84
85 let introspected = actix_multipart_extract::serde_introspect::<Self>();
87
88 match introspected.iter().position(|f| f == &field) {
89 Some(i) => max_sizes[i],
90 None => None
91 }
92 }
93 }
94 };
95
96 expanded.into()
97}