use beet_core::prelude::*;
use proc_macro2::TokenStream;
use syn::DeriveInput;
use syn::Expr;
use syn::ItemFn;
use syn::Result;
use syn::Type;
use syn::parse_quote;
pub struct NodeField<'a>(NamedField<'a>);
impl<'a> std::ops::Deref for NodeField<'a> {
type Target = NamedField<'a>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl<'a> NodeField<'a> {
pub fn parse_item_fn(input: &ItemFn) -> Result<Vec<NodeField<'_>>> {
NamedField::parse_item_fn(input)?
.into_iter()
.map(|field| {
Self::validate_keys(&field)?;
Ok(NodeField(field))
})
.collect()
}
pub fn parse_derive_input(
input: &DeriveInput,
) -> Result<Vec<NodeField<'_>>> {
NamedField::parse_derive_input(input)?
.into_iter()
.map(|field| {
Self::validate_keys(&field)?;
Ok(NodeField(field))
})
.collect()
}
fn validate_keys(named_field: &NamedField) -> Result<()> {
named_field.field_attributes.validate_allowed_keys(&[
"default",
"required",
"into",
"no_into",
"param",
"into_generics",
"into_func",
"into_type",
"flatten",
])?;
Ok(())
}
pub fn assign_tokens(
field: &NamedField,
) -> Result<(TokenStream, Type, Expr)> {
match field.inner_generics {
Some((seg, Type::TraitObject(obj))) if seg.ident == "Box" => {
let mut trait_bounds = obj.bounds.clone();
trait_bounds.push(parse_quote! { 'static });
Ok((
TokenStream::new(),
parse_quote! { impl #trait_bounds },
parse_quote! { Box::new(value) },
))
}
Some((seg, ty)) if seg.ident == "EventHandler" => Ok((
parse_quote! {<B:Bundle,M>},
parse_quote! { impl bevy::ecs::system::IntoObserverSystem<#ty,B,M> },
parse_quote! { EventHandler::new(value) },
)),
Some((seg, ty)) if seg.ident == "DerivedGetter" => Ok((
parse_quote! {<M>},
parse_quote! { impl IntoDerivedGetter<#ty,M> },
parse_quote! { value.into_derived_getter() },
)),
_ if let Some(ty) = field
.field_attributes
.get_value_parsed::<Type>("into_type")? =>
{
let generics = field
.field_attributes
.get_value_parsed::<TokenStream>("into_generics")?
.unwrap_or_default();
let func = field
.field_attributes
.get_value_parsed::<Expr>("into_func")?
.map(|func| {
parse_quote! { value.#func() }
})
.unwrap_or_else(|| {
parse_quote! { value.into() }
});
return Ok((generics, ty, func));
}
_ => {
let is_into = field.is_into();
let inner_ty = field.inner_ty;
match is_into {
true => Ok((
TokenStream::new(),
parse_quote! { impl Into<#inner_ty> },
parse_quote! { value.into() },
)),
false => Ok((
TokenStream::new(),
parse_quote! { #inner_ty },
parse_quote! { value },
)),
}
}
}
}
}