moxy-derive 0.0.4

derive macros for moxy crate
Documentation
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),*) }
            }
        }
    }
}