enum-helper-derive 0.3.0

Procedural macro implementation for enum-helper
Documentation
use proc_macro2::Span;
use quote::format_ident;

use crate::{
    ctxt::Ctxt,
    enum_all::{
        Ir, VariantIr,
        attr::{EnumAttr, VariantAttr},
    },
    gen_option::DefaultGenOption,
};

use super::GenOptions;

pub fn parse_ir(input: &syn::DeriveInput) -> syn::Result<Ir<'_>> {
    let syn::Data::Enum(data_enum) = &input.data else {
        return Err(syn::Error::new(
            Span::call_site(),
            "EnumAll only supports enum",
        ));
    };

    let cx = Ctxt::new();

    let enum_attr = EnumAttr::from_attrs(&cx, &input.attrs);

    let mut variants = Vec::new();
    let mut is_unit_enum = true;

    for variant in &data_enum.variants {
        let v_ir = parse_variant_ir(&cx, variant);

        let is_unit = matches!(v_ir.fields, syn::Fields::Unit);

        if !v_ir.skip && !is_unit {
            is_unit_enum = false;
        }
        variants.push(v_ir);
    }

    let all_enabled = enum_attr.all.enabled_or(true);

    if !is_unit_enum && all_enabled {
        let e = syn::Error::new(
            Span::call_site(),
            "EnumAll cannot build `ALL` for non-unit variants; consider `#[enum_all(all(disable))]` to derive only `COUNT`, or `#[enum_all(skip)]` to skip them",
        );
        cx.syn_error(e);
    };

    let gen_options = make_gen_options(input, enum_attr);

    cx.check()?;

    Ok(Ir {
        ident: &input.ident,
        generics: &input.generics,
        variants,
        gen_options,
    })
}

fn parse_variant_ir(cx: &Ctxt, variant: &syn::Variant) -> VariantIr {
    let ident = variant.ident.clone();

    let attr = VariantAttr::from_attrs(cx, &variant.attrs);

    VariantIr {
        ident,
        fields: variant.fields.clone(),
        skip: attr.skip.get(),
    }
}

fn make_gen_options(input: &syn::DeriveInput, enum_attr: EnumAttr) -> GenOptions {
    let opt = |ident: &str| DefaultGenOption {
        enabled: true,
        ident: format_ident!("{}", ident),
        vis: input.vis.clone(),
    };

    let const_all = enum_attr.all.into_gen_option(opt("ALL"));
    let const_count = enum_attr.count.into_gen_option(opt("COUNT"));

    GenOptions {
        const_all,
        const_count,
    }
}