actix-type-handler 0.1.0

actix-type-handler is a procedural macro crate for Actix-web designed to simplify API creation. It enhances code readability by allowing direct struct argument passing, error handling with Result, and unique argument and return types for each API, while providing flexibility to define custom error responses.
Documentation
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Ident, Item, ReturnType, Pat};

#[proc_macro_attribute]
pub fn type_handler(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as Item);

    let fn_item = if let Item::Fn(item) = ast {
        item
    } else {
        panic!("The attribute should be applied to functions.");
    };

    let fn_name = &fn_item.sig.ident;
    let api_fn_name = Ident::new(&format!("{}_api", fn_name), fn_name.span());

    let args = &fn_item.sig.inputs;

    let _return_type = match &fn_item.sig.output {
        ReturnType::Type(_, ty) => match &**ty {
            syn::Type::Path(path) => &path.path.segments[0].ident,
            _ => panic!("The function must return a Result."),
        },
        _ => panic!("The function must return a Result."),
    };

    let mut _request_type = None;
    let mut new_args = Vec::new();
    let mut call_args = Vec::new();

    for arg in args.iter() {
        if let syn::FnArg::Typed(pat_type) = arg {
            let arg_name = pat_type.pat.clone();
            let arg_type = &*pat_type.ty;
            if let Pat::Ident(ref pat_ident) = *arg_name {
                if pat_ident.ident == "query" {
                    _request_type = Some(arg_type);
                    new_args.push(quote! {actix_web::web::Query(query): actix_web::web::Query<#_request_type>});
                    call_args.push(quote! {query});
                } else if pat_ident.ident == "body" {
                    _request_type = Some(arg_type);
                    new_args.push(quote! {body: actix_web::web::Json<#_request_type>});
                    call_args.push(quote! {body.into_inner()});

				} else if pat_ident.ident == "path" {
					_request_type = Some(arg_type);
					new_args.push(quote! {path: actix_web::web::Path<#_request_type>});
					call_args.push(quote! {path.into_inner()});
				} else{
					new_args.push(quote! {#arg_name: #arg_type});
					call_args.push(quote! {#arg_name});
				}
            }
        }
    }

    let output = quote! {
        #fn_item

        pub async fn #api_fn_name(#(#new_args),*) -> impl actix_web::Responder {
            match #fn_name(#(#call_args),*).await {
                Ok(d) => {
                    let res = ApiResponse { message: "".to_string(), data: d };
                    actix_web::HttpResponse::Ok().json(res)
                }
                Err(e) => { e.error_response() }
            }
        }
    };

    output.into()
}