vise-macros 0.3.2

Procedural macros for `vise` crate
Documentation
//! `register` attribute macro.

use std::fmt;

use proc_macro::TokenStream;
use quote::quote;
use syn::{Attribute, ItemStatic, Path};

use crate::utils::{metrics_attribute, ParseAttribute};

/// `#[metrics(..)]` attributes on registered static items.
#[derive(Default)]
struct RegistrationAttrs {
    cr: Option<Path>,
}

impl fmt::Debug for RegistrationAttrs {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("RegistrationAttrs")
            .field("cr", &self.cr.as_ref().map(|_| "_"))
            .finish()
    }
}

impl ParseAttribute for RegistrationAttrs {
    fn parse(raw: &Attribute) -> syn::Result<Self> {
        let mut attrs = Self::default();
        raw.parse_nested_meta(|meta| {
            if meta.path.is_ident("crate") {
                attrs.cr = Some(meta.value()?.parse()?);
                Ok(())
            } else {
                Err(meta.error("unsupported attribute"))
            }
        })?;
        Ok(attrs)
    }
}

fn register_static(input: &mut ItemStatic) -> syn::Result<proc_macro2::TokenStream> {
    let attrs: RegistrationAttrs = metrics_attribute(&input.attrs)?;
    input
        .attrs
        .retain(|attr| !attr.meta.path().is_ident("metrics"));

    let cr = if let Some(cr) = &attrs.cr {
        quote!(#cr)
    } else {
        quote!(vise)
    };
    let name = &input.ident;

    Ok(quote! {
        const _: () = {
            #[#cr::_reexports::ctor]
            fn register_metric() {
                #cr::METRICS_REGISTRATIONS.push(&#name);
            }
        };
    })
}

pub(crate) fn impl_register(input: TokenStream) -> TokenStream {
    let mut item: ItemStatic = match syn::parse(input) {
        Ok(item) => item,
        Err(err) => return err.into_compile_error().into(),
    };

    match register_static(&mut item) {
        Ok(registration) => quote! {
            #item
            #registration
        }
        .into(),
        Err(err) => err.into_compile_error().into(),
    }
}