matched_enums_macro 1.3.0

Contains the macro for matchable enums.
Documentation
extern crate alloc;

use alloc::vec::Vec;
use proc_macro2::Span;
use syn::{Generics, Ident, ItemEnum, Meta, parse2, spanned::Spanned};

use super::{TopLevelAttribute, Variant};

pub struct Enumeration {
    pub attribute: TopLevelAttribute,
    pub generics: Generics,
    pub ident: Ident,
    span: Span,
    variants: Vec<Variant>,
}

impl Enumeration {
    pub fn span(&self) -> Span {
        self.span
    }

    pub fn variants(&self) -> &[Variant] {
        &self.variants
    }
}

impl TryFrom<ItemEnum> for Enumeration {
    type Error = syn::Error;

    fn try_from(value: ItemEnum) -> syn::Result<Self> {
        const ATTR_NAME: &str = "matched_enum";

        let mut enumeration = Self {
            attribute: value
                .attrs
                .iter()
                .find(|attr| attr.path().is_ident(ATTR_NAME))
                .map(|attr| match &attr.meta {
                    Meta::List(members) => parse2::<TopLevelAttribute>(members.tokens.clone()),
                    _ => Err(syn::Error::new(
                        attr.span(),
                        format_args!(
                        "Must provide {ATTR_NAME} as a list. (e.g., `#[{ATTR_NAME}(key=val, ..)]`)"
                    ),
                    )),
                })
                .unwrap_or(Ok(TopLevelAttribute::default()))?,
            generics: value.generics.clone(),
            ident: value.ident.clone(),
            span: value.span(),
            variants: Vec::with_capacity(value.variants.len()),
        };

        if enumeration.attribute.value_types.is_empty() {
            return Err(syn::Error::new(
                enumeration.span(),
                "`value_types` may not be empty",
            ));
        }

        for variant in value.variants {
            enumeration.variants.push(Variant::try_from(variant)?);
        }

        Ok(enumeration)
    }
}

#[cfg(test)]
mod test;