matched_enums_macro 1.1.0

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

use convert_case::{Case, Casing};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Ident, ItemEnum, Type};

use alloc::string::ToString;
use alloc::vec::Vec;

use crate::implementation::VariantMatcherPair;

fn ident_to_snake(ident: &Ident) -> Ident {
    let span = ident.span();
    let new_name = ident.to_string().to_case(Case::Snake);
    Ident::new(&new_name, span)
}

fn make_matcher_ident(enum_type: &ItemEnum) -> Ident {
    format_ident!("{}Matcher", enum_type.ident)
}

pub fn declare_runtime_configurable(enum_type: &ItemEnum) -> TokenStream {
    let enum_ident = &enum_type.ident;
    let matcher_ident = make_matcher_ident(enum_type);

    let variants = enum_type.variants.iter().clone().map(|e| &e.ident);

    let attribute_idents = variants.clone().map(ident_to_snake).collect::<Vec<Ident>>();

    quote! {
        struct #matcher_ident<T>{
            _convert_from_type: core::marker::PhantomData<T>,
            #(pub #attribute_idents: alloc::boxed::Box<dyn Fn(&T) -> bool>),*,
        }

        impl<T> #matcher_ident<T> {
            pub fn try_into_enum(&self, value: &T) -> core::result::Result<#enum_ident, ::matched_enums_types::UnmatchedError>{
                #(if (self.#attribute_idents)(value) {
                    return Ok(#enum_ident::#variants);
                 }
                )*

                Err(::matched_enums_types::UnmatchedError{})
            }
        }

    }
}

pub fn implement_runtime_configurable(
    enum_type: &ItemEnum,
    type_spec: &Type,
    match_pairs: &[VariantMatcherPair],
) -> TokenStream {
    let matcher_ident = make_matcher_ident(enum_type);

    let match_arms = match_pairs
        .iter()
        .clone()
        .map(|VariantMatcherPair(_, attr)| &attr.arm);

    let attribute_idents = match_pairs
        .iter()
        .clone()
        .map(|VariantMatcherPair(ident, _)| ident_to_snake(ident));

    quote! {
        impl Default for #matcher_ident<#type_spec> {
            fn default() -> Self {
                Self{
                    #(#attribute_idents: alloc::boxed::Box::new(|&value| matches!(value, #match_arms))),*,
                    _convert_from_type: Default::default(),
                }
            }
        }
    }
}