in pyo3_special_method_derive_macro/src/str_repr.rs [249:434]
fn generate_fmt_impl_for_enum(
data_enum: &syn::DataEnum,
name: &Ident,
is_repr: bool,
string_formatter: Option<&Vec<Attribute>>,
macro_name: &str,
) -> syn::Result<proc_macro2::TokenStream> {
let variants = data_enum.variants.iter().collect::<Vec<_>>();
let mut ident_formatter = quote! { #DEFAULT_ENUM_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 arms = variants.iter().map(|variant| {
let ident = &variant.ident;
let (to_skip, display_attr) = {
let mut to_skip = false;
let mut display_attr = None;
for attr in &variant.attrs {
let path = attr.path();
if path.is_ident(ATTR_SKIP_NAMESPACE) {
let _ = attr.parse_nested_meta(|meta| {
to_skip |= meta.path.is_ident(macro_name) || meta.path.is_ident(SKIP_ALL);
Ok(())
});
if path.is_ident(ATTR_NAMESPACE_FORMATTER) {
display_attr = Some(attr);
}
}
}
(to_skip, 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;
}
}
// If {} is not in ident_fmt, we must not format ident.
// If {} is not in variant_fmt, we don't use stringify! either
match &variant.fields {
Fields::Unit => {
let formatters = variant_fmt.to_string().matches("{}").count()
- variant_fmt.to_string().matches("{{}}").count();
let variant_formatter = if formatters == 1 {
quote! { &format!(#variant_fmt, stringify!(#ident)) }
} else if formatters == 0 {
quote! { &format!(#variant_fmt) }
} else {
return Err(syn::Error::new(data_enum.enum_token.span(), "Specify 1 (variant), or 0 formatters in the format string."));
};
Ok(if !to_skip {
quote! {
Self::#ident => repr += #variant_formatter,
}
} else {
quote! {
Self::#ident => repr += "<variant skipped>",
}
})
}
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
// Tuple variant with one field
// TODO now that we have AutoDisplay we want this
Ok(if !to_skip {
quote! { #name::#ident(ref single) => {#ident_formatter;} }
} else {
quote! {
#ident => repr += "<variant skipped>",
}
})
}
Fields::Named(fields) => {
let mut field_names: Vec<(&Option<Ident>, String, usize)> = Vec::new();
for field in &fields.named {
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_enum.enum_token.span(), "Specify 1 (variant), or 0 formatters in the format string."));
};
let formatter_str = variant_fmt.to_string();
field_names.push((&field.ident, formatter_str[1..formatter_str.len()-1].to_string(), formatters));
}
let mut format_string = "{}(".to_string();
let formatter = if is_repr { quote! { fmt_debug } } else { quote! { fmt_display } };
for (i, (name, formatter, _n_formatters)) in field_names.iter().enumerate() {
if i == 0 {
format_string = format!("{format_string}{}={}", name.as_ref().unwrap(), formatter);
} else {
format_string = format!("{format_string}, {}={}", name.as_ref().unwrap(), formatter);
}
}
format_string = format!("{format_string})");
Ok(if !to_skip {
let mut names = Vec::new();
for (name, _, n_formatters) in field_names.clone() {
if n_formatters > 0 {
names.push(quote! { #name.#formatter() });
}
}
let mut new_field_names = Vec::new();
for (name, _, _) in field_names.clone() {
new_field_names.push(name);
}
quote! {
Self::#ident { #(#new_field_names),* } => repr += &format!(#format_string, stringify!(#ident), #(#names),*),
}
} else {
let mut names = Vec::new();
for (name, _, _) in field_names.clone() {
names.push(quote! { #name });
}
quote! {
Self::#ident { #(#names),* } => {
let _ = (#(#names),*);
repr += "<variant skipped>";
}
}
})
}
_ => {
// Default case: stringify the variant name
Ok(quote! { &format!("{}", stringify!(#ident)); })
}
}
}).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_enum.enum_token.span(),
"Specify 2 (name, repr), 1 (name), or 0 formatters in the format string.",
));
};
Ok(quote! {
let mut repr = "".to_string();
match self {
#(#arms)*
}
let repr = #ident_formatter;
})
}