1use proc_macro::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::{spanned::Spanned, ReturnType, Type};
4
5fn looks_like_result(return_type: &ReturnType) -> bool {
6 if let ReturnType::Type(_, ty) = return_type {
7 if let Type::Path(p) = &**ty {
8 if let Some(seg) = p.path.segments.last() {
9 if seg.ident == "Result" {
10 return true;
11 }
12 }
13 }
14 }
15
16 false
17}
18
19#[proc_macro_attribute]
31pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
32 let input = syn::parse_macro_input!(item as syn::ItemFn);
33
34 let ret = &input.sig.output;
35 let inputs = &input.sig.inputs;
36 let name = &input.sig.ident;
37 let body = &input.block;
38 let attrs = &input.attrs;
39 let vis = &input.vis;
40
41 if name != "main" {
42 return TokenStream::from(quote_spanned! { name.span() =>
43 compile_error!("only the main function can be tagged with #[cgi::main]"),
44 });
45 }
46
47 if input.sig.asyncness.is_some() {
48 return TokenStream::from(quote_spanned! { input.span() =>
49 compile_error!("async not supported"),
50 });
51 }
52
53 let inner = if looks_like_result(ret) {
54 quote! {
55 cgi::handle(|request: cgi::Request| {
56 match inner_main(request) {
57 Ok(resp) => resp,
58 Err(err) => {
59 eprintln!("{:?}", err);
60 cgi::empty_response(500)
61 }
62 }
63 })
64 }
65 } else {
66 quote! {
67 cgi::handle(inner_main)
68 }
69 };
70
71 let result = quote! {
72 #vis fn main() {
73 #(#attrs)*
74 fn inner_main(#inputs) #ret {
75 #body
76 }
77
78 #inner
79 }
80
81 };
82
83 result.into()
84}