support max_size attribute

This commit is contained in:
Rob Ede 2020-08-23 15:38:42 +01:00
parent 9161f279de
commit 90b122c999
No known key found for this signature in database
GPG Key ID: C2A3B36E841A91E6
5 changed files with 60 additions and 24 deletions

View File

@ -10,6 +10,7 @@ proc-macro = true
[dependencies] [dependencies]
quote = "1" quote = "1"
syn = { version = "1", features = ["extra-traits"] } syn = { version = "1", features = ["extra-traits"] }
proc-macro2 = "1"
# [dev-dependencies] # [dev-dependencies]
actix-multipart = "0.3.0-beta.1" actix-multipart = "0.3.0-beta.1"

View File

@ -2,6 +2,7 @@ use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{ use syn::{
parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, Ident, parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, Ident,
Meta, MetaList, MetaNameValue, NestedMeta, Path,
}; };
#[proc_macro_derive(MultipartForm, attributes(multipart))] #[proc_macro_derive(MultipartForm, attributes(multipart))]
@ -29,12 +30,45 @@ pub fn derive(input: TokenStream) -> TokenStream {
}); });
let field_max_sizes = fields.iter().map(|f| { let field_max_sizes = fields.iter().map(|f| {
let Field { ident, .. } = f; let Field { ident, attrs, .. } = f;
// TODO: parse field attributes find for attr in attrs {
// #[multipart(max_size = n)] // TODO: use something like https://github.com/TedDriggs/darling ??
quote! { stringify!(#ident) => Some(8096) } if let Ok(m) = attr.parse_meta() {
if let Meta::List(MetaList { path, nested, .. }) = m {
if path.get_ident().unwrap()
!= &Ident::new("multipart", proc_macro2::Span::call_site())
{
continue;
}
// it's our meta list, marked by multipart
if let Some(NestedMeta::Meta(Meta::NameValue(MetaNameValue {
path: Path { segments, .. },
lit,
..
}))) = nested.first()
{
for seg in segments {
// if there's a max_size attr in the list, extract the lit
if &seg.ident
== &Ident::new(
"max_size",
proc_macro2::Span::call_site(),
)
{
// TODO: ensure literal is numeric
return quote! { stringify!(#ident) => Some(#lit) };
}
}
}
}
}
}
quote! { stringify!(#ident) => None }
}); });
let build_fields = fields.iter().map(|f| { let build_fields = fields.iter().map(|f| {

View File

@ -6,7 +6,7 @@ use bytes::BytesMut;
struct Form { struct Form {
name: String, name: String,
#[multipart(max_size = 8096)] #[multipart(max_size = 1024)]
file: BytesMut, file: BytesMut,
} }

View File

@ -0,0 +1,18 @@
use bytes::{BufMut, Bytes, BytesMut};
pub trait BuildFromBytes {
fn append(&mut self, next: Bytes);
}
impl BuildFromBytes for String {
fn append(&mut self, chunk: Bytes) {
let chunk_str = std::str::from_utf8(&chunk).expect("string field is not utf-8");
self.push_str(chunk_str);
}
}
impl BuildFromBytes for BytesMut {
fn append(&mut self, chunk: Bytes) {
self.put(&chunk[..]);
}
}

View File

@ -1,27 +1,10 @@
#![allow(clippy::borrow_interior_mutable_const)] #![allow(clippy::borrow_interior_mutable_const)]
mod byte_builder;
mod error; mod error;
mod extractor; mod extractor;
mod server; mod server;
pub use self::byte_builder::BuildFromBytes;
pub use self::error::MultipartError; pub use self::error::MultipartError;
pub use self::server::{Field, Multipart}; pub use self::server::{Field, Multipart};
use bytes::{BufMut, Bytes, BytesMut};
pub trait BuildFromBytes {
fn append(&mut self, next: Bytes);
}
impl BuildFromBytes for String {
fn append(&mut self, chunk: Bytes) {
let chunk_str = std::str::from_utf8(&chunk).expect("string field is not utf-8");
self.push_str(chunk_str);
}
}
impl BuildFromBytes for BytesMut {
fn append(&mut self, chunk: Bytes) {
self.put(&chunk[..]);
}
}