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