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
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{
	parse::{Parse, ParseStream, Result as SynResult},
	punctuated::Punctuated,
	token::Comma,
	Generics,
	Ident,
	ItemStruct,
	Path,
	parenthesized,
	parse_macro_input
};

struct MimeList(Punctuated<Path, Comma>);

impl Parse for MimeList
{
	fn parse(input: ParseStream) -> SynResult<Self>
	{
		let content;
		let _paren = parenthesized!(content in input);
		let list : Punctuated<Path, Comma> = Punctuated::parse_separated_nonempty(&content)?;
		Ok(Self(list))
	}
}

#[cfg(not(feature = "openapi"))]
fn impl_openapi_type(_ident : &Ident, _generics : &Generics) -> TokenStream2
{
	quote!()
}

#[cfg(feature = "openapi")]
fn impl_openapi_type(ident : &Ident, generics : &Generics) -> TokenStream2
{
	let krate = super::krate();
	
	quote! {
		impl #generics #krate::OpenapiType for #ident #generics
		{
			fn schema() -> #krate::OpenapiSchema
			{
				use #krate::{export::openapi::*, OpenapiSchema};
				
				OpenapiSchema::new(SchemaKind::Type(Type::String(StringType {
					format: VariantOrUnknownOrEmpty::Item(StringFormat::Binary),
					pattern: None,
					enumeration: Vec::new()
				})))
			}
		}
	}
}

pub fn expand_request_body(tokens : TokenStream) -> TokenStream
{
	let krate = super::krate();
	let input = parse_macro_input!(tokens as ItemStruct);
	let ident = input.ident;
	let generics = input.generics;
	
	let types : Vec<Path> = input.attrs.into_iter().filter(|attr|
		attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("supported_types".to_string()) // TODO wtf
	).flat_map(|attr| {
		let m : MimeList = syn::parse2(attr.tokens).expect("unable to parse attributes");
		m.0.into_iter()
	}).collect();
	
	let types = match types {
		ref types if types.is_empty() => quote!(None),
		types => quote!(Some(vec![#(#types),*]))
	};
	
	let impl_openapi_type = impl_openapi_type(&ident, &generics);
	
	let output = quote! {
		impl #generics #krate::RequestBody for #ident #generics
		where #ident #generics : #krate::FromBody
		{
			fn supported_types() -> Option<Vec<#krate::Mime>>
			{
				#types
			}
		}
		
		#impl_openapi_type
	};
	output.into()
}