debug_utilities 0.1.0

debug_utilities provides procedural macros to simplify code debugging
Documentation
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{quote, format_ident};
use syn::{parse_macro_input, ItemFn, FnArg, Pat, PatIdent, Ident};

#[proc_macro]
pub fn dbg_here(input: TokenStream) -> TokenStream {
    let input_parsed = parse_macro_input!(input as Ident);
    let name = input_parsed.to_string();

    let expanded: TokenStream;

    if name == "" {
        expanded = quote! {
            {
                use colored::{self, Colorize};

                let message = format!("\n[{}:{}] in {}\n", file!(), line!(), module_path!()).blue();
                eprintln!("{}", message);
                #input_parsed
            }
        }.into();
    } else {
        expanded = quote! {
            {
                use colored::{self, Colorize};

                let message = format!("\n[{}:{}] in {}: '{}' = {:?}\n",
                    file!(),
                    line!(),
                    module_path!(),
                    #name,
                    &#input_parsed
                ).blue();
                eprintln!("{}", message);
                #input_parsed
            }
        }.into();
    }

    TokenStream::from(expanded)
}

#[proc_macro_attribute]
pub fn trace(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let input_fn = parse_macro_input!(item as ItemFn);
    
    let fn_name = &input_fn.sig.ident;
    let fn_vis = &input_fn.vis;
    let fn_block = &input_fn.block;
    let fn_generics = &input_fn.sig.generics;
    let fn_inputs = &input_fn.sig.inputs;
    let fn_output = &input_fn.sig.output;
    let fn_asyncness = &input_fn.sig.asyncness;
    let fn_constness = &input_fn.sig.constness;
    let fn_unsafety = &input_fn.sig.unsafety;
    let fn_abi = &input_fn.sig.abi;
    
    let arg_names: Vec<_> = fn_inputs.iter().enumerate().filter_map(|(i, arg)| {
        match arg {
            FnArg::Typed(pat_typed) => {
                if let Pat::Ident(PatIdent { ident, .. }) = &*pat_typed.pat {
                    Some(ident.clone())
                } else {
                    let ident = format_ident!("arg_{}", i);
                    Some(ident)
                }
            }
            FnArg::Receiver(_) => {
                let ident = format_ident!("self");
                Some(ident)
            }
        }
    }).collect();
    
    let log_args = arg_names.iter().map(|name| {
        quote! {
            let message_param = format!("    Param '{}' = {:?}\n", stringify!(#name), #name).blue();
            println!("{}", message_param);
        }
    });
    
    let expanded = quote! {
        use colored::{self, Colorize};

        #fn_vis #fn_constness #fn_asyncness #fn_unsafety #fn_abi fn #fn_name #fn_generics(#fn_inputs) #fn_output {
            let message_enter = format!("\n--> Entering function: '{}'", stringify!(#fn_name)).blue();
            println!("{}", message_enter);
            #(#log_args)*
            
            let result = {
                #fn_block
            };
            
            let message_exit = format!("\n<-- Exiting function: '{}' with result: {:?}\n", stringify!(#fn_name), result).blue();
            println!("{}", message_exit);
            result
        }
    };
    
    TokenStream::from(expanded)
}