1mod fs;
2use proc_macro::TokenStream;
3use quote::{quote, ToTokens};
4use syn::{ItemFn, FnArg, punctuated::Punctuated, token::Comma, Attribute};
5
6#[derive(Clone, Copy)]
9enum When {
10 Call,
11 Return,
12}
13
14#[proc_macro_attribute]
15pub fn param(args: TokenStream, func: TokenStream) -> TokenStream {
16 parse(args, func, When::Call)
17}
18
19#[proc_macro_attribute]
20pub fn debug(args: TokenStream, func: TokenStream) -> TokenStream {
21 parse(args, func, When::Return)
22}
23
24#[proc_macro]
25pub fn read_dir(args: TokenStream) -> TokenStream {
26 fs::read_dir(args)
27}
28
29fn parse(_: TokenStream, func: TokenStream, when: When) -> TokenStream {
30 let func = syn::parse_macro_input!(func as ItemFn);
31 let func_attrs = func.attrs; let attrs = parse_attrs(func_attrs);
33 let func_vis = &func.vis; let func_block = &func.block; let sig = &func.sig;
37 let func_constness = &sig.constness; let func_async = &sig.asyncness; let func_abi = &sig.abi; let func_name = &sig.ident; let func_generics = &sig.generics; let func_where_clause = &func_generics.where_clause; let func_inputs = &sig.inputs; let func_output = &sig.output; let params = parse_params(func_inputs);
47 let (format, values) = get_log_format_values(&func_name.to_string(), params);
48 let format = match when {
49 When::Call => format!("call {format}"),
50 When::Return => format!("called {format}"),
51 };
52 let log = match (when, func_output) {
53 (When::Call, _) => quote! {
54 macro_log::d!(#format, #values);
55 },
56 (_, syn::ReturnType::Default) => quote! {
57 macro_log::d!("{}", call);
58 },
59 (_, syn::ReturnType::Type(_, _)) => quote! {
60 macro_log::d!("{} => {:?}", call, return_value);
61 },
62 };
63
64 let caller = match when {
65 When::Call => quote! {
66 #attrs
67 #func_vis #func_constness #func_async #func_abi fn #func_name #func_generics(#func_inputs) #func_output #func_where_clause {
68 #log
69 #func_block
70 }
71 },
72 When::Return => quote! {
73 #attrs
74 #func_vis #func_constness #func_async #func_abi fn #func_name #func_generics(#func_inputs) #func_output #func_where_clause {
75 let call = format!(#format, #values);
76 let return_value = #func_block;
77 #log
78 return_value
79 }
80 },
81 };
82 caller.into()
84}
85
86fn parse_attrs(attrs: Vec<Attribute>) -> proc_macro2::TokenStream {
88 attrs
89 .iter()
90 .map(|attr| attr.to_token_stream().to_string())
91 .collect::<Vec<String>>().join(" ")
92 .parse().unwrap()
93}
94
95fn parse_params(func_inputs: &Punctuated<FnArg, Comma>) -> Vec<String> {
97 let mut args = vec![];
98 for arg in func_inputs.into_iter() {
99 match arg {
100 FnArg::Receiver(arg) => {
102 let tokens = quote!(#arg).to_string();
104 let (name, _vartype) = tokens.split_once(" : ").unwrap();
106 args.push(name.into());
107 }
108 FnArg::Typed(_) => { let tokens = quote!(#arg).to_string();
111 let (name, _vartype) = tokens.split_once(" : ").unwrap();
113 args.push(name.into());
114 }
115 }
116 }
117 args
118}
119
120fn get_log_format_values(func_name: &str, args: Vec<String>) -> (String, proc_macro2::TokenStream) {
122 let format_args = args.iter()
123 .map(|it| if it.as_str() != "_" {
124 format!("{it} = {{:?}}")
125 } else {
126 "_ = ?".to_string()
127 })
128 .collect::<Vec<String>>().join(", ");
129 let format = format!("fn {func_name}({format_args})");
130 let values = args.iter()
133 .filter(|it| it.as_str() != "_")
134 .map(|it| format!("{it}")).collect::<Vec<String>>().join(",");
135 let values = values.parse::<proc_macro2::TokenStream>().unwrap();
136 (format, values)
139}