use proc_macro::{Delimiter, TokenStream, TokenTree};
pub fn profile_attr_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut tokens: Vec<TokenTree> = item.into_iter().collect();
let mut fn_name: Option<String> = None;
let mut body_idx: Option<usize> = None;
let mut i = 0;
while i < tokens.len() {
match &tokens[i] {
TokenTree::Ident(ident) if ident.to_string() == "fn" => {
if i + 1 < tokens.len() {
if let TokenTree::Ident(name) = &tokens[i + 1] {
fn_name = Some(name.to_string());
}
}
}
TokenTree::Group(g) if g.delimiter() == Delimiter::Brace => {
body_idx = Some(i);
break;
}
_ => {}
}
i += 1;
}
let fn_name = match fn_name {
Some(name) => name,
None => {
return "compile_error!(\"#[profile] can only be applied to functions\")"
.parse()
.unwrap()
}
};
let body_idx = match body_idx {
Some(idx) => idx,
None => {
return "compile_error!(\"#[profile] requires a function with a body\")"
.parse()
.unwrap()
}
};
let original_body = if let TokenTree::Group(g) = &tokens[body_idx] {
g.stream()
} else {
return "compile_error!(\"Expected function body\")".parse().unwrap();
};
let new_body_src = format!(
r#"{{
let _logwise_profile_guard = {{
let (id, mut record) = logwise::hidden::profile_begin_pre(file!(), line!(), column!());
let name = concat!(module_path!(), "::", "{fn_name}");
record.log(name);
logwise::hidden::profile_begin_post(id, record, name)
}};
{{ {original_body} }}
}}"#,
fn_name = fn_name,
original_body = original_body
);
let new_body: TokenStream = new_body_src.parse().unwrap();
let new_body_group = new_body.into_iter().next().unwrap();
tokens[body_idx] = new_body_group;
tokens.into_iter().collect()
}