use proc_macro::TokenStream;
use quote::quote;
use syn::{Expr, ItemFn, LitStr, Token, parse_macro_input};
pub fn armour_metrics_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr_parsed = parse_macro_input!(attr as AttrArgs);
let prefix = attr_parsed.prefix.value();
let mut fn_item: ItemFn = parse_macro_input!(item as ItemFn);
let fn_name = fn_item.sig.ident.to_string();
let duration_metric = format!("{prefix}_{fn_name}_duration");
let total_metric = format!("{prefix}_{fn_name}_total");
let original_block = &fn_item.block;
let name_expr = &attr_parsed.name;
let new_block = quote! {
{
struct _ArmourMetricsGuard {
start: ::std::time::Instant,
name: &'static str,
}
impl ::std::ops::Drop for _ArmourMetricsGuard
{
fn drop(&mut self) {
histogram!(#duration_metric, "name" => self.name)
.record(self.start.elapsed().as_secs_f64());
counter!(#total_metric, "name" => self.name).increment(1);
}
}
let _armour_metrics_guard = _ArmourMetricsGuard {
start: ::std::time::Instant::now(),
name: #name_expr,
};
let _ = &_armour_metrics_guard;
#original_block
}
};
*fn_item.block = syn::parse2(new_block).unwrap();
quote! { #fn_item }.into()
}
#[derive(Debug)]
struct AttrArgs {
prefix: LitStr,
name: Expr,
}
impl syn::parse::Parse for AttrArgs {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let prefix_kw: syn::Ident = input.parse()?;
if !prefix_kw.to_string().as_str().eq("prefix") {
return Err(input.error("expected 'prefix'"));
}
let _eq: Token![=] = input.parse()?;
let prefix: LitStr = input.parse()?;
let _comma: Token![,] = input.parse()?;
let name_kw: syn::Ident = input.parse()?;
if !name_kw.to_string().as_str().eq("name") {
return Err(input.error("expected 'name'"));
}
let _eq: Token![=] = input.parse()?;
let name: Expr = input.parse()?;
Ok(AttrArgs { prefix, name })
}
}