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(),
}
}
}
}
}