1#[cfg(feature = "time")]
20#[macro_use]
21extern crate quote;
22#[cfg(feature = "time")]
23#[macro_use]
24extern crate syn;
25extern crate proc_macro;
26
27#[cfg(feature = "time")]
28const DEFAULT_LEVEL: &str = "debug";
29#[cfg(feature = "time")]
30const DEFAULT_NAME_PATTERN: &str = "{}";
31
32#[cfg(feature = "time")]
33fn extract_literal(token_tree: &proc_macro::TokenTree) -> String {
34 let s = match token_tree {
35 proc_macro::TokenTree::Literal(literal) => literal.to_string(),
36 _ => panic!(
37 "Invalid argument. Specify at most two string literal arguments, for log level and name pattern, in that order."
38 ),
39 };
40
41 let s = s.trim().trim_matches('"').trim().to_string();
43 s
44}
45
46#[cfg(feature = "time")]
49fn get_log_level_and_name_pattern(metadata: proc_macro::TokenStream) -> (String, String) {
50 let macro_args: Vec<proc_macro::TokenTree> = metadata
53 .into_iter()
54 .filter(|token| match token {
55 proc_macro::TokenTree::Literal(_) => true,
56 _ => false,
57 })
58 .collect();
59
60 if macro_args.is_empty() {
61 return (DEFAULT_LEVEL.to_string(), DEFAULT_NAME_PATTERN.to_string());
62 }
63
64 if macro_args.len() > 2 {
65 panic!("Specify at most two string literal arguments, for log level and name pattern");
66 }
67
68 let first_arg = extract_literal(¯o_args[0]);
69
70 if first_arg.contains("{}") && macro_args.len() == 2 {
71 panic!("Invalid first argument. Specify the log level as the first argument and the pattern as the second.");
72 }
73
74 let first_arg_lower = first_arg.to_ascii_lowercase();
75 if macro_args.len() == 1 {
76 match first_arg_lower.as_str() {
77 "error" | "warn" | "info" | "debug" | "trace" | "never" => {
78 return (first_arg_lower, DEFAULT_NAME_PATTERN.to_string());
80 }
81 _ => {
82 return (DEFAULT_LEVEL.to_string(), first_arg.to_string());
87 }
88 }
89 }
90
91 match first_arg_lower.as_str() {
94 "error" | "warn" | "info" | "debug" | "trace" | "never" => {
95 let mut second_arg = extract_literal(¯o_args[1]);
96 if second_arg.is_empty() {
97 second_arg += DEFAULT_NAME_PATTERN;
98 }
99
100 return (first_arg_lower, second_arg.to_string());
101 }
102 _ => {
103 panic!("Invalid first argument. Specify the log level as the first argument and the pattern as the second.")
104 }
105 }
106}
107
108#[cfg(feature = "time")]
109fn get_timer_name(name_pattern: &str, function_name: &str) -> String {
110 let function_name_with_parenthesis = format!("{}()", function_name);
111 let timer_name = name_pattern.replacen("{}", &function_name_with_parenthesis, 1);
112 timer_name
113}
114
115#[cfg(feature = "time")]
133#[proc_macro_attribute]
134pub fn time(metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
135 let (level, name_pattern) = get_log_level_and_name_pattern(metadata);
136
137 if level != "never" {
138 let input_fn: syn::ItemFn = parse_macro_input!(input as syn::ItemFn);
139 let visibility = input_fn.vis;
140 let ident = input_fn.sig.ident;
141 let inputs = input_fn.sig.inputs;
142 let output = input_fn.sig.output;
143 let generics = &input_fn.sig.generics;
144 let where_clause = &input_fn.sig.generics.where_clause;
145 let block = input_fn.block;
146
147 let timer_name = get_timer_name(&name_pattern, &ident.to_string());
148
149 (quote!(
150 #visibility fn #ident #generics (#inputs) #output #where_clause {
151 let _tmr = ::aleo_std::prelude::timer!(#timer_name);
152 #block
153 }
154 ))
155 .into()
156 } else {
157 proc_macro::TokenStream::from(input).into()
158 }
159}
160
161#[cfg(not(feature = "time"))]
162#[proc_macro_attribute]
163pub fn time(_metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
164 input
165}