use crate::err;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use quote_into::quote_into;
use syn::spanned::Spanned;
pub(crate) fn enum_int(
mut item: syn::ItemEnum, args: TokenStream,
) -> syn::Result<TokenStream2> {
let ty = syn::parse::<syn::Path>(args)?;
for attr in item.attrs.iter() {
if attr.path().is_ident("repr") {
return err!(attr.span(), "remove the #[repr(...)] attr");
}
}
item.attrs.push(syn::parse_quote! { #[repr(#ty)] });
let mut brs = TokenStream2::new();
let ident = &item.ident;
let mut index = 0usize;
let mut discr: Option<(&syn::Expr, Option<usize>)> = None;
for v in item.variants.iter() {
let syn::Fields::Unit = v.fields else {
return err!(v.span(), "all variants must be a unit");
};
if let Some((_, exp)) = &v.discriminant {
index = 0;
if let syn::Expr::Lit(lit) = exp {
let syn::Lit::Int(int) = &lit.lit else {
return err!(exp.span(), "only numbers are allowed");
};
let val = int.base10_parse::<usize>()?;
discr = Some((exp, Some(val)));
} else {
discr = Some((exp, None));
}
}
let vi = &v.ident;
match discr {
Some((e, None)) => {
let idx = proc_macro2::Literal::usize_unsuffixed(index);
quote_into!(brs += v if v == #e + #idx => Self::#vi,);
}
Some((_, Some(base))) => {
let idx = proc_macro2::Literal::usize_unsuffixed(index + base);
quote_into!(brs += #idx => Self::#vi,);
}
None => {
let idx = proc_macro2::Literal::usize_unsuffixed(index);
quote_into!(brs += #idx => Self::#vi,);
}
}
index += 1;
}
Ok(quote! {
#item
impl From<#ident> for #ty {
fn from(value: #ident) -> Self {
value as #ty
}
}
impl From<#ty> for #ident {
fn from(value: #ty) -> Self {
match value {
#brs
_ => Default::default(),
}
}
}
})
}