fn generate_fmt_impl_for_struct()

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;
    })
}