matched_enums_macro 1.1.0

Contains the macro for matchable enums.
Documentation
#![no_std]

mod implementation;
mod match_arm;
mod match_attribute;
mod top_level_attribute;
mod utils;

extern crate proc_macro;

use core::panic;

#[cfg(feature = "runtime_configurable")]
mod runtime_configurable;

use proc_macro2::TokenStream;
use syn::{parse2, parse_macro_input, spanned::Spanned, ItemEnum, Meta};

use implementation::{get_match_attrs_per_type, implement_full, implement_partial};
use top_level_attribute::TopLevelAttribute;

#[cfg(feature = "runtime_configurable")]
use runtime_configurable::{declare_runtime_configurable, implement_runtime_configurable};

const ATTR_NAME: &str = "matched_enum";

fn derive_ranged_impl(enum_type: ItemEnum) -> syn::Result<TokenStream> {
    let attribute = enum_type
        .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(
                enum_type.span(),
                format_args!(
                    "Must provide {ATTR_NAME} as a list. (e.g., `#[{ATTR_NAME}(key=val, ..)]`)"
                ),
            )),
        })
        .unwrap_or(Ok(TopLevelAttribute::default()))?;

    if attribute.value_types.is_empty() {
        panic!("`value_types` may not be empty");
    }

    let matchers_per_type = get_match_attrs_per_type(&enum_type, &attribute)?;

    if matchers_per_type.is_empty() {
        panic!("Failed to register any type.");
    }

    let mut stream = TokenStream::default();

    #[cfg(feature = "runtime_configurable")]
    if attribute.allow_runtime_configurable {
        stream.extend(declare_runtime_configurable(&enum_type));
    }

    for (type_spec, match_pairs) in matchers_per_type.iter() {
        stream.extend(if attribute.use_partial_matching {
            implement_partial(&enum_type, type_spec, match_pairs)
        } else {
            implement_full(&enum_type, type_spec, match_pairs)
        });

        #[cfg(feature = "runtime_configurable")]
        if attribute.allow_runtime_configurable {
            stream.extend(implement_runtime_configurable(
                &enum_type,
                type_spec,
                match_pairs,
            ));
        }
    }

    Ok(stream)
}

#[proc_macro_derive(Matched, attributes(matches, matched_enum))]
pub fn derive_ranged(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
    derive_ranged_impl(parse_macro_input!(stream as ItemEnum))
        .unwrap_or_else(|err| panic!("{err}"))
        .into()
}