use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use venial::{GenericParamList, WhereClause};
use super::c_style_enum::CStyleEnum;
use super::godot_attribute::{GodotAttribute, ViaType};
use super::newtype::NewtypeStruct;
use crate::ParseResult;
use crate::util::bail;
pub struct GodotConvert {
pub ty_name: Ident,
pub where_clause: Option<WhereClause>,
pub generic_params: Option<GenericParamList>,
pub convert_type: ConvertType,
}
impl GodotConvert {
pub fn parse_declaration(item: venial::Item) -> ParseResult<Self> {
let data = ConvertType::parse_declaration(&item)?;
let (name, where_clause, generic_params) = match item {
venial::Item::Struct(struct_) => {
(struct_.name, struct_.where_clause, struct_.generic_params)
}
venial::Item::Enum(enum_) => {
if let Some(generic_params) = &enum_.generic_params {
return bail!(
generic_params,
"#[derive(GodotConvert)] does not support lifetimes or generic parameters on enums"
);
}
if let Some(where_clause) = &enum_.where_clause {
return bail!(
where_clause,
"#[derive(GodotConvert)] does not support where clauses"
);
}
(enum_.name, enum_.where_clause, enum_.generic_params)
}
other => {
return bail!(
other,
"#[derive(GodotConvert)] only supports structs and enums"
);
}
};
Ok(Self {
ty_name: name,
where_clause,
generic_params,
convert_type: data,
})
}
pub fn generic_args(&self) -> Option<venial::InlineGenericArgs<'_>> {
self.generic_params
.as_ref()
.map(|params| params.as_inline_args())
}
pub fn ensure_no_generics(&self, derive_name: &str) -> ParseResult<()> {
if let Some(generic_params) = &self.generic_params {
return bail!(
generic_params,
"#[derive({derive_name})] does not support lifetimes or generic parameters"
);
}
if let Some(where_clause) = &self.where_clause {
return bail!(
where_clause,
"#[derive({derive_name})] does not support where clauses"
);
}
Ok(())
}
}
pub enum ConvertType {
NewType { field: NewtypeStruct },
Enum { variants: CStyleEnum, via: ViaType },
}
impl ConvertType {
pub fn parse_declaration(item: &venial::Item) -> ParseResult<Self> {
let attribute = GodotAttribute::parse_attribute(item)?;
match item {
venial::Item::Struct(struct_) => {
let GodotAttribute::Transparent { .. } = attribute else {
return bail!(
attribute.span(),
"#[derive(GodotConvert)] on structs currently only works with #[godot(transparent)]"
);
};
Ok(Self::NewType {
field: NewtypeStruct::parse_struct(struct_)?,
})
}
venial::Item::Enum(enum_) => {
let GodotAttribute::Via { via_type, .. } = attribute else {
return bail!(
attribute.span(),
"#[derive(GodotConvert)] on enums requires #[godot(via = ...)]"
);
};
Ok(Self::Enum {
variants: CStyleEnum::parse_enum(enum_)?,
via: via_type,
})
}
_ => unreachable!(), }
}
pub fn via_type(&self) -> TokenStream {
match self {
ConvertType::NewType { field } => field.sized.ty.to_token_stream(),
ConvertType::Enum { via, .. } => via.to_token_stream(),
}
}
}