#[cfg(feature = "time")]
#[macro_use]
extern crate quote;
#[cfg(feature = "time")]
#[macro_use]
extern crate syn;
extern crate proc_macro;
#[cfg(feature = "time")]
const DEFAULT_LEVEL: &str = "debug";
#[cfg(feature = "time")]
const DEFAULT_NAME_PATTERN: &str = "{}";
#[cfg(feature = "time")]
fn extract_literal(token_tree: &proc_macro::TokenTree) -> String {
let s = match token_tree {
proc_macro::TokenTree::Literal(literal) => literal.to_string(),
_ => panic!(
"Invalid argument. Specify at most two string literal arguments, for log level and name pattern, in that order."
),
};
let s = s.trim().trim_matches('"').trim().to_string();
s
}
#[cfg(feature = "time")]
fn get_log_level_and_name_pattern(metadata: proc_macro::TokenStream) -> (String, String) {
let macro_args: Vec<proc_macro::TokenTree> = metadata
.into_iter()
.filter(|token| match token {
proc_macro::TokenTree::Literal(_) => true,
_ => false,
})
.collect();
if macro_args.is_empty() {
return (DEFAULT_LEVEL.to_string(), DEFAULT_NAME_PATTERN.to_string());
}
if macro_args.len() > 2 {
panic!("Specify at most two string literal arguments, for log level and name pattern");
}
let first_arg = extract_literal(¯o_args[0]);
if first_arg.contains("{}") && macro_args.len() == 2 {
panic!("Invalid first argument. Specify the log level as the first argument and the pattern as the second.");
}
let first_arg_lower = first_arg.to_ascii_lowercase();
if macro_args.len() == 1 {
match first_arg_lower.as_str() {
"error" | "warn" | "info" | "debug" | "trace" | "never" => {
return (first_arg_lower, DEFAULT_NAME_PATTERN.to_string());
}
_ => {
return (DEFAULT_LEVEL.to_string(), first_arg.to_string());
}
}
}
match first_arg_lower.as_str() {
"error" | "warn" | "info" | "debug" | "trace" | "never" => {
let mut second_arg = extract_literal(¯o_args[1]);
if second_arg.is_empty() {
second_arg += DEFAULT_NAME_PATTERN;
}
return (first_arg_lower, second_arg.to_string());
}
_ => {
panic!("Invalid first argument. Specify the log level as the first argument and the pattern as the second.")
}
}
}
#[cfg(feature = "time")]
fn get_timer_name(name_pattern: &str, function_name: &str) -> String {
let function_name_with_parenthesis = format!("{}()", function_name);
let timer_name = name_pattern.replacen("{}", &function_name_with_parenthesis, 1);
timer_name
}
#[cfg(feature = "time")]
#[proc_macro_attribute]
pub fn time(metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (level, name_pattern) = get_log_level_and_name_pattern(metadata);
if level != "never" {
let input_fn: syn::ItemFn = parse_macro_input!(input as syn::ItemFn);
let visibility = input_fn.vis;
let ident = input_fn.sig.ident;
let inputs = input_fn.sig.inputs;
let output = input_fn.sig.output;
let generics = &input_fn.sig.generics;
let where_clause = &input_fn.sig.generics.where_clause;
let block = input_fn.block;
let timer_name = get_timer_name(&name_pattern, &ident.to_string());
(quote!(
#visibility fn #ident #generics (#inputs) #output #where_clause {
let _tmr = ::aleo_std::prelude::timer!(#timer_name);
#block
}
))
.into()
} else {
proc_macro::TokenStream::from(input).into()
}
}
#[cfg(not(feature = "time"))]
#[proc_macro_attribute]
pub fn time(_metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
input
}