use crate::{
MetricsField, MetricsFieldKind, NameStyle, RootAttributes, enums::MetricsVariant, metric_name,
};
use proc_macro2::{Span, TokenStream as Ts2};
use quote::{quote, quote_spanned};
use syn::Ident;
pub(crate) fn generate_value_impl_for_enum(
root_attrs: &RootAttributes,
value_name: &Ident,
parsed_variants: &[MetricsVariant],
) -> Ts2 {
let variants_and_strings = parsed_variants.iter().map(|variant| {
let variant_ident = &variant.ident;
let metric_name = metric_name(root_attrs, root_attrs.rename_all, variant);
quote_spanned!(variant.ident.span()=> #value_name::#variant_ident => #metric_name)
});
quote!(
impl ::std::convert::From<&'_ #value_name> for &'static str {
fn from(value: &#value_name) -> Self {
#[allow(deprecated)] match value {
#(#variants_and_strings),*
}
}
}
impl ::std::convert::From<#value_name> for &'static str {
fn from(value: #value_name) -> Self {
<&str as ::std::convert::From<&_>>::from(&value)
}
}
impl ::metrique::writer::Value for #value_name {
fn write(&self, writer: impl ::metrique::writer::ValueWriter) {
writer.string(::std::convert::Into::<&str>::into(self));
}
}
impl ::metrique::writer::core::SampleGroup for #value_name {
fn as_sample_group(&self) -> ::std::borrow::Cow<'static, str> {
::std::borrow::Cow::Borrowed(::std::convert::Into::<&str>::into(self))
}
}
)
}
pub fn validate_value_impl_for_struct(
root_attrs: &RootAttributes,
value_name: &Ident,
parsed_fields: &[MetricsField],
) -> Result<(), syn::Error> {
let non_ignore_fields: Vec<&MetricsField> = parsed_fields
.iter()
.filter(|f| !matches!(f.attrs.kind, MetricsFieldKind::Ignore(_)))
.collect::<Vec<_>>();
if non_ignore_fields.len() > 1 {
return Err(syn::Error::new(
non_ignore_fields[1].span,
"multiple non-ignored fields for #[metrics(value)]",
));
}
for field in &non_ignore_fields {
if let MetricsFieldKind::Field {
unit: _,
sample_group,
name,
format: _,
} = &field.attrs.kind
{
if sample_group.is_some() {
return Err(syn::Error::new(
field.span,
"`sample_group` in value structs is used as a struct attribute, not a field attribute: `#[metrics(value, sample_group)]`",
));
}
if name.is_some() {
return Err(syn::Error::new(
field.span,
"`name` does not make sense with #[metrics(value)]",
));
}
}
}
if root_attrs.sample_group && non_ignore_fields.is_empty() {
return Err(syn::Error::new(
value_name.span(),
"`sample_group` requires a non-ignore field",
));
}
if root_attrs.emf_dimensions.is_some() {
return Err(syn::Error::new(
value_name.span(),
"emf_dimensions is not supported for #[metrics(value)]",
));
}
if root_attrs.prefix.is_some() {
return Err(syn::Error::new(
value_name.span(),
"prefix is not supported for #[metrics(value)]",
));
}
if !matches!(root_attrs.rename_all, NameStyle::Preserve) {
return Err(syn::Error::new(
value_name.span(),
"NameStyle is not supported for #[metrics(value)]",
));
}
Ok(())
}
pub(crate) fn format_value(format: &Option<syn::Path>, span: Span, field: Ts2) -> Ts2 {
if let Some(format) = format {
quote_spanned! { span=> &::metrique::format::FormattedValue::<_, #format, _>::new(#field)}
} else {
field
}
}
pub(crate) fn generate_value_impl_for_struct(
root_attrs: &RootAttributes,
value_name: &Ident,
parsed_fields: &[MetricsField],
) -> Result<Ts2, syn::Error> {
let mut non_ignore_fields_iter = parsed_fields
.iter()
.filter(|f| !matches!(f.attrs.kind, MetricsFieldKind::Ignore(_)));
let non_ignore_field = non_ignore_fields_iter.next();
assert!(
non_ignore_fields_iter.next().is_none(),
"value impl can't have multiple non-ignore fields"
);
let (body, sample_group_impl) = non_ignore_field
.map(|field| match &field.attrs.kind {
MetricsFieldKind::Field {
unit: _,
sample_group: _,
name: _,
format,
} => {
let ident = &field.ident;
let value = format_value(
format,
field.span,
quote_spanned! {field.span=> &self.#ident },
);
let sample_group_impl = if root_attrs.sample_group {
quote_spanned! {field.span=>
impl ::metrique::writer::core::SampleGroup for #value_name {
fn as_sample_group(&self) -> ::std::borrow::Cow<'static, str> {
#[allow(deprecated)] {
::metrique::writer::core::SampleGroup::as_sample_group(&self.#ident)
}
}
}
}
} else {
quote!{}
};
Ok((quote_spanned! {field.span=> ::metrique::writer::Value::write(#value, writer); }, sample_group_impl))
}
_ => Err(syn::Error::new(
field.span,
"only plain fields are supported in #[metrics(value)]",
)),
})
.transpose()?.unzip();
Ok(quote! {
impl ::metrique::writer::Value for #value_name {
fn write(&self, writer: impl ::metrique::writer::ValueWriter) {
#[allow(deprecated)] {
#body
}
}
}
#sample_group_impl
})
}