armour-derive 0.28.0

DDL and serialization for key-value storage
Documentation
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 })
    }
}