1#![doc(html_logo_url = "https://viz.rs/logo.svg")]
28#![doc(html_favicon_url = "https://viz.rs/logo.svg")]
29#![doc(test(
30 no_crate_inject,
31 attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
32))]
33#![cfg_attr(docsrs, feature(doc_cfg))]
34
35use proc_macro::TokenStream;
36use quote::quote;
37use syn::{FnArg, ItemFn, Result, ReturnType};
38
39#[proc_macro_attribute]
41pub fn handler(_args: TokenStream, input: TokenStream) -> TokenStream {
42 generate_handler(input).unwrap_or_else(|e| e.to_compile_error().into())
43}
44
45fn generate_handler(input: TokenStream) -> Result<TokenStream> {
46 let ast = syn::parse::<ItemFn>(input)?;
47 let vis = &ast.vis;
48 let docs = ast
49 .attrs
50 .iter()
51 .filter(|attr| attr.path().is_ident("doc"))
52 .cloned()
53 .collect::<Vec<_>>();
54 let name = ast.sig.ident.clone();
55 let asyncness = if ast.sig.asyncness.is_some() {
56 Some(quote!(.await))
57 } else {
58 None
59 };
60 let mut out = quote!(Ok(res));
61 let mut is_ok_type = false;
62 match &ast.sig.output {
63 ReturnType::Default => {
65 is_ok_type = true;
66 }
67 ReturnType::Type(_, ty) => match &**ty {
68 syn::Type::Path(path) => {
69 if let Some(seg) = &path.path.segments.first() {
70 is_ok_type = true;
71 if seg.ident == "Result" {
76 out = quote!(res);
77 }
78 }
79 }
80 syn::Type::ImplTrait(i) => {
81 if let Some(syn::TypeParamBound::Trait(d)) = &i.bounds.first() {
82 if matches!(d.path.get_ident(), Some(ident) if ident == "IntoResponse") {
85 is_ok_type = true;
86 }
87 }
88 }
89 syn::Type::Tuple(_) => {
90 is_ok_type = true;
92 }
93 _ => {
94 is_ok_type = false;
95 }
96 },
97 }
98
99 if !is_ok_type {
100 out = quote!();
101 }
102
103 let extractors =
104 ast.sig
105 .inputs
106 .clone()
107 .into_iter()
108 .fold(Vec::new(), |mut extractors, input| {
109 if let FnArg::Typed(pat) = input {
110 let ty = &pat.ty;
111 extractors
112 .push(quote!(<#ty as vidi_core::FromRequest>::extract(&mut req).await?));
113 }
114 extractors
115 });
116
117 let stream = quote! {
118 #(#docs)*
119 #[allow(non_camel_case_types)]
120 #[derive(Clone)]
121 #vis struct #name;
122
123 #[vidi_core::async_trait]
124 impl vidi_core::Handler<vidi_core::Request> for #name
125 {
126 type Output = vidi_core::Result<vidi_core::Response>;
127
128 #[allow(unused, unused_mut)]
129 async fn call(&self, mut req: vidi_core::Request) -> Self::Output {
130 #ast
131 let res = #name(#(#extractors),*)#asyncness;
132 #out.map(vidi_core::IntoResponse::into_response)
133 }
134 }
135 };
136
137 Ok(stream.into())
138}