actix_type_handler/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Ident, Item, ReturnType, Pat};
4
5#[proc_macro_attribute]
6pub fn type_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
7    let ast = parse_macro_input!(item as Item);
8
9    let fn_item = if let Item::Fn(item) = ast {
10        item
11    } else {
12        panic!("The attribute should be applied to functions.");
13    };
14
15    let fn_name = &fn_item.sig.ident;
16    let api_fn_name = Ident::new(&format!("{}_api", fn_name), fn_name.span());
17
18    let args = &fn_item.sig.inputs;
19
20    let _return_type = match &fn_item.sig.output {
21        ReturnType::Type(_, ty) => match &**ty {
22            syn::Type::Path(path) => &path.path.segments[0].ident,
23            _ => panic!("The function must return a Result."),
24        },
25        _ => panic!("The function must return a Result."),
26    };
27
28    let mut _request_type = None;
29    let mut new_args = Vec::new();
30    let mut call_args = Vec::new();
31
32    for arg in args.iter() {
33        if let syn::FnArg::Typed(pat_type) = arg {
34            let arg_name = pat_type.pat.clone();
35            let arg_type = &*pat_type.ty;
36            if let Pat::Ident(ref pat_ident) = *arg_name {
37                if pat_ident.ident == "query" {
38                    _request_type = Some(arg_type);
39                    new_args.push(quote! {actix_web::web::Query(query): actix_web::web::Query<#_request_type>});
40                    call_args.push(quote! {query});
41                } else if pat_ident.ident == "body" {
42                    _request_type = Some(arg_type);
43                    new_args.push(quote! {body: actix_web::web::Json<#_request_type>});
44                    call_args.push(quote! {body.into_inner()});
45
46				} else if pat_ident.ident == "path" {
47					_request_type = Some(arg_type);
48					new_args.push(quote! {path: actix_web::web::Path<#_request_type>});
49					call_args.push(quote! {path.into_inner()});
50				} else{
51					new_args.push(quote! {#arg_name: #arg_type});
52					call_args.push(quote! {#arg_name});
53				}
54            }
55        }
56    }
57
58    let output = quote! {
59        #fn_item
60
61        pub async fn #api_fn_name(#(#new_args),*) -> impl actix_web::Responder {
62            match #fn_name(#(#call_args),*).await {
63                Ok(d) => {
64                    let res = ApiResponse { message: "".to_string(), data: d };
65                    actix_web::HttpResponse::Ok().json(res)
66                }
67                Err(e) => { e.error_response() }
68            }
69        }
70    };
71
72    output.into()
73}
74