use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use quote::{ToTokens, quote};
use crate::ParseResult;
use crate::util::bail;
#[derive(Clone, Debug)]
pub struct CStyleEnum {
enumerator_names: Vec<Ident>,
enumerator_ords: Vec<TokenStream>,
}
impl CStyleEnum {
pub fn parse_enum(enum_: &venial::Enum) -> ParseResult<Self> {
let variants = enum_
.variants
.items()
.map(CStyleEnumerator::parse_enum_variant)
.collect::<ParseResult<Vec<_>>>()?;
let (names, ord_exprs) = Self::create_discriminant_mapping(variants)?;
Ok(Self {
enumerator_names: names,
enumerator_ords: ord_exprs,
})
}
fn create_discriminant_mapping(
enumerators: Vec<CStyleEnumerator>,
) -> ParseResult<(Vec<Ident>, Vec<TokenStream>)> {
let mut names = Vec::new();
let mut ord_exprs = Vec::new();
let mut last_ord = None;
for enumerator in enumerators.into_iter() {
let span = enumerator.discriminant_span();
let ord = match enumerator.discriminant {
Some(mut discriminant) => {
discriminant.set_span(span);
discriminant.to_token_stream()
}
None if last_ord.is_none() => quote! { 0 },
None => quote! { #last_ord + 1 },
};
last_ord = Some(ord.clone());
names.push(enumerator.name);
ord_exprs.push(ord)
}
Ok((names, ord_exprs))
}
pub fn enumerator_names(&self) -> &[Ident] {
&self.enumerator_names
}
pub fn enumerator_ord_exprs(&self) -> &[TokenStream] {
&self.enumerator_ords
}
}
#[derive(Clone, Debug)]
pub struct CStyleEnumerator {
name: Ident,
discriminant: Option<TokenTree>,
}
impl CStyleEnumerator {
fn parse_enum_variant(enum_variant: &venial::EnumVariant) -> ParseResult<Self> {
match enum_variant.fields {
venial::Fields::Unit => {}
_ => {
return bail!(
&enum_variant.fields,
"GodotConvert only supports C-style enums"
);
}
}
Ok(Self {
name: enum_variant.name.clone(),
discriminant: enum_variant.value.as_ref().map(|val| &val.value).cloned(),
})
}
fn discriminant_span(&self) -> Span {
match &self.discriminant {
Some(discriminant) => discriminant.span(),
None => self.name.span(),
}
}
}