matched_enums_macro 1.3.0

Contains the macro for matchable enums.
Documentation
use proc_macro2::TokenStream;

use crate::implementation::ImplementationContext;
use crate::models::Enumeration;

// NOTE: Reexported implementation helpers to make the external calls feature agnostic.
pub use implementation::{declare_runtime_configurable, implement_runtime_configurable};

#[cfg(feature = "runtime_configurable")]
mod implementation {
    extern crate alloc;

    use super::*;

    use convert_case::{Case, Casing};
    use quote::{format_ident, quote};
    use syn::Ident;

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

    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(ident: &Ident) -> Ident {
        format_ident!("{}Matcher", ident)
    }

    pub fn declare_runtime_configurable(enumeration: &Enumeration) -> syn::Result<TokenStream> {
        let enum_ident = &enumeration.ident;
        let matcher_ident = make_matcher_ident(&enumeration.ident);

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

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

        Ok(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(
        context: &ImplementationContext,
    ) -> syn::Result<TokenStream> {
        let ImplementationContext {
            ident,
            match_arms,
            value_type,
            variant_idents,
            ..
        } = context;
        let matcher_ident = make_matcher_ident(ident);

        let attribute_idents = variant_idents.iter().map(ident_to_snake);

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

#[cfg(not(feature = "runtime_configurable"))]
mod implementation {
    use super::*;

    pub fn declare_runtime_configurable(enumeration: &Enumeration) -> syn::Result<TokenStream> {
        Err(syn::Error::new(
            enumeration.span(),
            "Feature `runtime_configurable` must be enabled for to implement its behavior",
        ))
    }

    pub fn implement_runtime_configurable(
        context: &ImplementationContext,
    ) -> syn::Result<TokenStream> {
        Err(syn::Error::new(
            context.ident.span(),
            "Feature `runtime_configurable` must be enabled for to implement its behavior",
        ))
    }
}