use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::core::{Attrs, FieldName};
use super::DisplayOptions;
pub struct CustomFmtSyntax {
pub pattern: syn::LitStr,
pub exprs: Vec<syn::Expr>,
}
impl CustomFmtSyntax {
pub fn parse(attrs: &Attrs) -> syn::Result<Option<Self>> {
let display = attrs.get("display")?;
let display_attr = display.iter().find_map(|arg| arg.as_attr());
let pattern = display_attr.and_then(|attr| {
attr.args().iter().find_map(|arg| {
if arg.path().is_ident("__value") {
arg.as_lit().and_then(|lit| match lit {
syn::Lit::Str(s) => Some(s.clone()),
_ => None,
})
} else {
None
}
})
});
let Some(pattern) = pattern else {
return Ok(None);
};
let exprs: Vec<syn::Expr> = display_attr
.map(|attr| {
attr.args()
.iter()
.filter_map(|arg| arg.as_expr().cloned())
.collect()
})
.unwrap_or_default();
Ok(Some(Self { pattern, exprs }))
}
pub fn render(&self, opts: &DisplayOptions) -> TokenStream {
if opts.is_named {
let field_idents: Vec<_> = opts
.fields
.iter()
.filter_map(|f| match f.name() {
FieldName::Ident(id) => Some(id.clone()),
_ => None,
})
.collect();
let pattern = &self.pattern;
if opts.use_self {
if self.exprs.is_empty() {
quote! {
#[allow(unused)]
let Self { #(#field_idents,)* .. } = self;
::std::write!(f, #pattern)
}
} else {
let exprs = &self.exprs;
quote! {
#[allow(unused)]
let Self { #(#field_idents,)* .. } = self;
::std::write!(f, #pattern, #(#exprs),*)
}
}
} else if self.exprs.is_empty() {
quote! { ::std::write!(f, #pattern) }
} else {
let exprs = &self.exprs;
quote! { ::std::write!(f, #pattern, #(#exprs),*) }
}
} else if opts.use_self {
let pattern = &self.pattern;
if self.exprs.is_empty() {
let field_indices: Vec<_> = opts.fields.iter().map(|f| f.name().clone()).collect();
quote! { ::std::write!(f, #pattern, #(self.#field_indices,)*) }
} else {
let exprs = &self.exprs;
quote! { ::std::write!(f, #pattern, #(#exprs),*) }
}
} else {
let pattern = &self.pattern;
if self.exprs.is_empty() {
let field_names: Vec<_> = opts
.fields
.iter()
.enumerate()
.map(|(i, _)| format_ident!("__v{}", i))
.collect();
quote! { ::std::write!(f, #pattern, #(#field_names,)*) }
} else {
let exprs = &self.exprs;
quote! { ::std::write!(f, #pattern, #(#exprs),*) }
}
}
}
}