use proc_macro2::TokenStream;
use quote::quote;
use crate::{
Render,
core::{Attrs, Field},
params,
};
#[cfg(feature = "color")]
use super::rules::color::ColorSyntax;
use super::rules::{DisplayOptions, custom::CustomFmtSyntax, style::StyleSyntax};
#[derive(Clone, Default)]
pub struct StructSyntax;
impl Render for StructSyntax {
type Args = params::StructParams;
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 attributes = Attrs::parse(&args.input.attrs)?;
let fields: Vec<_> = args
.data
.fields
.iter()
.enumerate()
.map(|(i, field)| Field::parse(i, field))
.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!(args.data.fields, syn::Fields::Named(_));
let is_unit = matches!(args.data.fields, syn::Fields::Unit);
let alias = attributes
.get("display")?
.iter()
.find_map(|a| a.as_attr())
.and_then(|attr| attr.get_string("alias"));
let name = alias.unwrap_or_else(|| ident.to_string());
if is_unit || visible_fields.is_empty() {
let body = quote! { ::std::write!(f, #name) };
return 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 {
#body
}
}
});
}
if let Some(custom) = CustomFmtSyntax::parse(&attributes)? {
let opts = DisplayOptions {
name,
fields: &visible_fields,
is_named,
use_self: true,
};
let body = custom.render(&opts);
return 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 {
#body
}
}
});
}
let opts = DisplayOptions {
name,
fields: &visible_fields,
is_named,
use_self: true,
};
let style = StyleSyntax::parse(&attributes)?;
#[allow(unused_mut)]
let mut output = style.render(&opts)?;
#[allow(unused_mut)]
let mut color_import = quote!();
#[cfg(feature = "color")]
{
let color = ColorSyntax::parse(&attributes)?;
color_import = color.import_tokens();
output = color.render(output);
}
let body = quote! { #output };
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
#body
}
}
})
}
}