ok_macros 2.0.1

The standard library that's ok
Documentation
// impls.rs
use {
    proc_macro::TokenStream,
    quote::quote,
    syn::{
        parse::{Parse, ParseStream},
        parse_macro_input,
        punctuated::Punctuated,
        Data,
        DeriveInput,
        Fields,
        Token,
    },
};

struct ImplsArgs {
    traits: Punctuated<syn::Path, Token![,]>,
}

impl Parse for ImplsArgs {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(ImplsArgs {
            traits: input.parse_terminated(syn::Path::parse, Token![,])?,
        })
    }
}

pub fn impls(args: TokenStream, input: TokenStream) -> TokenStream {
    let args = parse_macro_input!(args as ImplsArgs);
    let input = parse_macro_input!(input as DeriveInput);

    let Data::Enum(data_enum) = input.data.clone() else {
        return quote! {
            compile_error!("impls can only be used on enums");
        }
        .into();
    };

    let trait_checks = args.traits.iter().collect::<Vec<_>>();

    let checks = data_enum.variants.iter().flat_map(|variant| {
        let variant_name = &variant.ident;
        let attrs = &variant.attrs;

        match &variant.fields {
            Fields::Unnamed(fields) => {
                fields.unnamed.iter().flat_map(|field| {
                    let ty = &field.ty;

                    let trait_bounds = trait_checks.iter().map(|trait_path| {
                        quote! {
                            #(#attrs)*
                            const _: () = {
                                trait AssertImpl where #ty: #trait_path {}
                            };
                        }
                    });
                    trait_bounds.collect::<Vec<_>>()
                }).collect::<Vec<_>>()
            },
            _ => {
                vec![quote! {
                    compile_error!(concat!("Variant ", stringify!(#variant_name), " must be a tuple variant"));
                }]
            }
        }
    });

    let expanded = quote! {
        #input
        #(#checks)*
    };

    expanded.into()
}