batpak-macros 0.10.0

Procedural macros for the batpak event-sourcing store.
Documentation
//! `#[derive(AllVariants)]` — generate `pub const ALL: [Self; N]` for a
//! fieldless enum.
//!
//! Replaces a hand-maintained `const ALL = [Self::A, Self::B, ...]` slice (which
//! silently drifts when a variant is added) with one generated from the enum in
//! declaration order. Applies only to enums whose variants are ALL unit
//! variants — a data-carrying variant has no canonical zero-arg value to list,
//! so it is a compile error rather than a guess.

use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Fields};

/// Expand `#[derive(AllVariants)]` for `input`.
pub(crate) fn expand(input: &DeriveInput) -> syn::Result<TokenStream> {
    let Data::Enum(data) = &input.data else {
        return Err(syn::Error::new(
            input.ident.span(),
            "#[derive(AllVariants)] supports enums only",
        ));
    };

    let mut idents = Vec::with_capacity(data.variants.len());
    for variant in &data.variants {
        if !matches!(variant.fields, Fields::Unit) {
            return Err(syn::Error::new(
                variant.span(),
                "#[derive(AllVariants)] requires every variant to be a unit variant",
            ));
        }
        idents.push(&variant.ident);
    }

    let ident = &input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    let count = idents.len();

    Ok(quote! {
        impl #impl_generics #ident #ty_generics #where_clause {
            /// Every variant, in declaration order. Generated by
            /// `#[derive(AllVariants)]`, so it can never drift from the enum.
            pub const ALL: [Self; #count] = [ #(Self::#idents),* ];
        }
    })
}