#![allow(clippy::needless_return)]
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{ItemFn, Signature, LitStr, Token};
#[proc_macro_attribute]
pub fn instrument(args: TokenStream, tokens: TokenStream) -> TokenStream {
let input: ItemFn = syn::parse_macro_input!(tokens as ItemFn);
let args: TimedArgs = syn::parse_macro_input!(args as TimedArgs);
let name = args.name.unwrap_or_else(|| input.sig.ident.to_string());
let ItemFn {
attrs,
vis,
block,
sig,
..
} = input;
let Signature {
output: return_type,
inputs: params,
unsafety,
asyncness,
constness,
abi,
ident,
generics:
syn::Generics {
params: gen_params,
where_clause,
..
},
..
} = sig;
let stream = quote!(
#(#attrs) *
#vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #return_type
#where_clause
{
time_graph::spanned!(#name, {
#block
})
}
);
return stream.into();
}
struct TimedArgs {
name: Option<String>,
}
mod kw {
syn::custom_keyword!(name);
}
impl Parse for TimedArgs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut args = TimedArgs {
name: None,
};
while !input.is_empty() {
let lookahead = input.lookahead1();
if lookahead.peek(kw::name) {
if args.name.is_some() {
return Err(input.error("expected only a single `name` argument"));
}
let _ = input.parse::<kw::name>()?;
let _ = input.parse::<Token![=]>()?;
args.name = Some(input.parse::<LitStr>()?.value());
} else if lookahead.peek(LitStr) {
if args.name.is_some() {
return Err(input.error("expected only a single `name` argument"));
}
args.name = Some(input.parse::<LitStr>()?.value());
} else {
return Err(lookahead.error());
}
}
Ok(args)
}
}