newnum_proc_macros 0.13.1

Procedural macros reexported by the deprecated `newnum` crate
Documentation
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{spanned::Spanned, Data, Error, Type};

pub fn derive_eval_fields(
    input: &syn::DeriveInput,
    trait_ident: &str,
    variant_attr: &str,
    mut eval: impl FnMut(&Type) -> TokenStream,
) -> proc_macro2::TokenStream {
    match &input.data {
        Data::Struct(data) => {
            let members = data.fields.members();
            let member_values = data.fields.iter().map(|field| eval(&field.ty));

            quote! {
                Self {#(
                    #members: #member_values,
                )*}
            }
        }
        Data::Enum(data) => {
            let selected_variants = data
                .variants
                .iter()
                .filter(|variant| {
                    variant
                        .attrs
                        .iter()
                        .any(|attr| attr.path().is_ident(variant_attr))
                })
                .collect::<Vec<_>>();

            if selected_variants.len() != 1 {
                return Error::new(
                    selected_variants.get(2).span(),
                    format!("`{variant_attr}` must be used on exactly one variant"),
                )
                .to_compile_error()
                .into();
            }

            let variant = selected_variants.into_iter().next().unwrap();

            let variant_ident = &variant.ident;

            let members = variant.fields.members();
            let member_values = variant.fields.iter().map(|field| eval(&field.ty));

            quote! {
                Self::#variant_ident {#(
                    #members: #member_values,
                )*}
            }
        }
        Data::Union(_) => Error::new(
            Span::call_site(),
            format!("`{trait_ident}` cannot be derived for unions"),
        )
        .to_compile_error()
        .into(),
    }
}