in pyo3_special_method_derive_macro/src/str_repr.rs [71:203]
fn generate_fmt_impl_for_struct(
data_struct: &syn::DataStruct,
name: &Ident,
is_repr: bool,
string_formatter: Option<&Vec<Attribute>>,
macro_name: &str,
) -> syn::Result<proc_macro2::TokenStream> {
let mut ident_formatter = quote! { #DEFAULT_STRUCT_IDENT_FORMATTER };
if let Some(attrs) = string_formatter {
for attr in attrs {
if attr.path().is_ident(ATTR_NAMESPACE_FORMATTER) {
if let Some(formatter) = find_display_attribute(attr) {
ident_formatter = formatter;
break;
}
break;
}
}
}
let fields = &data_struct.fields;
let fields = fields
.iter()
.filter(|f| {
// Default `is_skip` based on the field's visibility
let mut to_skip = !matches!(f.vis, Visibility::Public(_));
for attr in &f.attrs {
let path = attr.path();
if attr.path().is_ident(ATTR_SKIP_NAMESPACE) {
// only parse ATTR_SKIP_NAMESPACE and not [serde] or [default]
let _ = attr.parse_nested_meta(|meta| {
to_skip |= meta.path.is_ident(macro_name) || meta.path.is_ident(SKIP_ALL);
Ok(())
});
break;
} else if path.is_ident(ATTR_NAMESPACE_NO_FMT_SKIP) {
// Explicitly mark to not skip the field
to_skip = false;
break;
}
}
!to_skip
})
.collect::<Vec<_>>();
let field_fmts = fields
.iter()
.enumerate()
.map(|(i, field)| {
let display_attr = {
let mut display_attr = None;
for attr in &field.attrs {
let path = attr.path();
if path.is_ident(ATTR_NAMESPACE_FORMATTER) {
display_attr = Some(attr);
}
}
display_attr
};
let mut variant_fmt = quote! { #DEFAULT_ELEMENT_FORMATTER };
if let Some(display_attr) = display_attr {
if let Some(formatter) = find_display_attribute(display_attr) {
variant_fmt = formatter;
}
}
let formatters = variant_fmt.to_string().matches("{}").count()
- variant_fmt.to_string().matches("{{}}").count();
if formatters > 1 {
return Err(syn::Error::new(data_struct.struct_token.span(), "Specify 1 (variant), or 0 formatters in the format string."));
};
let formatter_str = variant_fmt.to_string();
let format_str = format!("{{}}={}{{}}", &formatter_str[1..formatter_str.len()-1]);
let postfix = if i + 1 < fields.len() { ", " } else { "" };
let formatter = if is_repr { quote! { fmt_debug } } else { quote! { fmt_display } };
Ok(match &field.ident {
Some(ident) => {
if formatters > 0 {
quote! {
repr += &format!(#format_str, stringify!(#ident), self.#ident.#formatter(), #postfix);
}
} else {
quote! {
repr += &format!(#format_str, stringify!(#ident), #postfix);
}
}
}
None => {
// If the field doesn't have a name, we generate a name based on its index
let index = syn::Index::from(i);
if formatters > 0 {
quote! {
repr += &format!(#format_str, stringify!(#index), self.#index.#formatter(), #postfix);
}
} else {
quote! {
repr += &format!(#format_str, stringify!(#index), #postfix);
}
}
}
})
})
.collect::<syn::Result<Vec<_>>>()?;
// Handle any escaped {}
let formatters = ident_formatter.to_string().matches("{}").count()
- ident_formatter.to_string().matches("{{}}").count();
let ident_formatter = if formatters == 2 {
quote! { format!(#ident_formatter, stringify!(#name), repr) }
} else if formatters == 1 {
quote! { format!(#ident_formatter, stringify!(#name)) }
} else if formatters == 0 {
quote! { format!(#ident_formatter) }
} else {
return Err(syn::Error::new(
data_struct.struct_token.span(),
"Specify 2 (name, repr), 1 (name), or 0 formatters in the format string.",
));
};
Ok(quote! {
let mut repr = "".to_string();
#(#field_fmts)*
let repr = #ident_formatter;
})
}