use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::{
Render,
core::{Attrs, Field, FieldName},
params,
};
#[cfg(feature = "color")]
use super::rules::color::ColorSyntax;
use super::rules::{DisplayOptions, custom::CustomFmtSyntax, style::StyleSyntax};
#[derive(Clone, Default)]
pub struct EnumSyntax;
impl Render for EnumSyntax {
type Args = params::EnumParams;
fn render(&self, args: Self::Args) -> syn::Result<TokenStream> {
let ident = &args.input.ident;
let (impl_generics, type_generics, where_generics) = args.input.generics.split_for_impl();
let enum_attrs = Attrs::parse(&args.input.attrs)?;
let enum_style = StyleSyntax::parse(&enum_attrs)?;
let mut arms = Vec::new();
#[allow(unused_mut)]
let mut color_import = quote!();
#[cfg(feature = "color")]
let enum_color = ColorSyntax::parse(&enum_attrs)?;
for variant in &args.data.variants {
let variant_ident = &variant.ident;
let variant_attrs = Attrs::parse(&variant.attrs)?;
let fields: Vec<_> = variant
.fields
.iter()
.enumerate()
.map(|(i, f)| Field::parse(i, f))
.collect::<syn::Result<Vec<_>>>()?;
let visible_fields = fields
.iter()
.map(|f| -> syn::Result<Option<&Field>> {
let field_display = f.attrs().get("display")?;
let field_attr = field_display.iter().find_map(|a| a.as_attr());
Ok(if field_attr.map(|a| a.exists("skip")).unwrap_or(false) {
None
} else {
Some(f)
})
})
.collect::<syn::Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect::<Vec<_>>();
let is_named = matches!(variant.fields, syn::Fields::Named(_));
let is_unit = matches!(variant.fields, syn::Fields::Unit);
let v_style = StyleSyntax::parse(&variant_attrs)?;
let mut style = if v_style.is_default() && !enum_style.is_default() {
StyleSyntax::parse(&enum_attrs)?
} else {
v_style
};
if enum_style.is_pretty() {
style = style.with_pretty(true);
}
let alias = variant_attrs
.get("display")?
.iter()
.find_map(|a| a.as_attr())
.and_then(|attr| attr.get_string("alias"));
let name_str = alias.unwrap_or_else(|| variant_ident.to_string());
let (pattern, body) = if is_unit {
let pattern = quote! { Self::#variant_ident };
let body = quote! { ::std::write!(f, #name_str) };
(pattern, body)
} else if is_named {
let visible_idents: Vec<_> = visible_fields
.iter()
.filter_map(|f| match f.name() {
FieldName::Ident(id) => Some(id.clone()),
_ => None,
})
.collect();
let pattern = quote! { Self::#variant_ident { #(#visible_idents,)* .. } };
if visible_fields.is_empty() {
(pattern, quote! { ::std::write!(f, #name_str) })
} else if let Some(custom) = CustomFmtSyntax::parse(&variant_attrs)? {
let opts = DisplayOptions {
name: name_str,
fields: &visible_fields,
is_named: true,
use_self: false,
};
(pattern, custom.render(&opts))
} else {
let opts = DisplayOptions {
name: name_str,
fields: &visible_fields,
is_named: true,
use_self: false,
};
#[allow(unused_mut)]
let mut output = style.render(&opts)?;
#[cfg(feature = "color")]
{
let v_color = ColorSyntax::parse(&variant_attrs)?;
let color = if v_color.theme.is_some() {
&v_color
} else {
&enum_color
};
if color.needs_import() {
color_import = color.import_tokens();
}
output = color.render(output);
}
(pattern, quote! { #output })
}
} else {
let bindings: Vec<_> = fields
.iter()
.enumerate()
.map(|(i, _)| format_ident!("__v{}", i))
.collect();
let pattern = quote! { Self::#variant_ident(#(#bindings),*) };
if visible_fields.is_empty() {
(pattern, quote! { ::std::write!(f, #name_str) })
} else if let Some(custom) = CustomFmtSyntax::parse(&variant_attrs)? {
let opts = DisplayOptions {
name: name_str,
fields: &visible_fields,
is_named: false,
use_self: false,
};
(pattern, custom.render(&opts))
} else {
let opts = DisplayOptions {
name: name_str,
fields: &visible_fields,
is_named: false,
use_self: false,
};
#[allow(unused_mut)]
let mut output = style.render(&opts)?;
#[cfg(feature = "color")]
{
let v_color = ColorSyntax::parse(&variant_attrs)?;
let color = if v_color.theme.is_some() {
&v_color
} else {
&enum_color
};
if color.needs_import() {
color_import = color.import_tokens();
}
output = color.render(output);
}
(pattern, quote! { #output })
}
};
arms.push(quote! { #pattern => { #body } });
}
Ok(quote! {
impl #impl_generics ::std::fmt::Display for #ident #type_generics #where_generics {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
#color_import
match self {
#(#arms,)*
}
}
}
})
}
}