use proc_macro::{
TokenStream,
TokenTree::{self, *},
};
use cg2::{Generator, f};
use pm2_types::EnumRepr;
use crate::{get_idents, get_repr, seek_ident};
pub fn run(_attr: TokenStream, mut item: TokenStream) -> TokenStream {
let item_tokens: Vec<TokenTree> = item.clone().into_iter().collect();
let mut item_stream = item.clone().into_iter();
let repr = if let Some(r) = get_repr(&item_tokens) {
match EnumRepr::try_from(r.as_str()) {
Ok(r) => Some(r),
Err(r) => panic!("unknown repr: {r}"),
}
} else {
None
};
if !seek_ident(&mut item_stream, "enum") {
panic!("missing enum keyword");
}
let Ident(i) = item_stream.next().expect("missing name") else {
panic!("name not an Ident");
};
let name = i.to_string();
let Group(body) = item_stream.next().expect("missing body") else {
panic!("generic params and where clauses are not allowed");
};
let body_tokens: Vec<TokenTree> = body.stream().into_iter().collect();
let variants = get_enum_variants(&body_tokens);
if variants.is_empty() {
panic!("must have atleast 1 variant");
}
let mut g = Generator::new();
g.implement()
.space()
.ident(&name)
.block(|g: &mut Generator| {
cg2::bind!($g, declare_const);
declare_const!(pub REPR: Option<&str>, |g: &mut Generator| {
if let Some(r) = repr {
g.out(f!("Some({:?})", r.as_str()));
} else {
g.out("None");
}
});
declare_const!(pub NUM_VARIANTS: usize = variants.len());
declare_const!(pub FIRST_VARIANT: Self = f!("Self::{}", variants.first().unwrap()));
declare_const!(pub LAST_VARIANT: Self = f!("Self::{}", variants.last().unwrap()));
if let Some(r) = repr
&& r.is_primitive()
{
declare_const!(pub MAX_DISCRIMINANT: r.as_str(), "Self::LAST_VARIANT as _");
}
});
item.extend::<TokenStream>(g.into_code().parse().expect("failed to parse new code"));
item
}
pub fn get_enum_variants(tt: &[TokenTree]) -> Vec<String> {
get_idents(tt)
}