lyra-engine/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs

271 lines
8.2 KiB
Rust

use quote::quote;
use syn::{Token, parenthesized, punctuated::Punctuated};
struct Field {
name: syn::Ident,
_eq: Token![=],
ty: syn::Path,
}
impl syn::parse::Parse for Field {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
Ok(Self {
name: input.parse()?,
_eq: input.parse()?,
ty: input.parse()?,
})
}
}
struct SimpleStruct {
type_path: syn::Path,
pub generics: syn::Generics,
fields: Vec<Field>,
}
impl syn::parse::Parse for SimpleStruct {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
//let type_path = syn::Path::parse_mod_style(input)?;
let type_path: syn::Path = input.parse()?;
/* let mut generics = input.parse::<syn::Generics>()?;
generics.where_clause = input.parse()?; */
let mut fields = vec![];
// parse fields if a comma is found
if input.peek(Token![,]) {
let _: Token![,] = input.parse()?;
let ident: syn::Ident = input.parse()?;
let ident_str = ident.to_string();
match ident_str.as_str() {
"fields" => {
let content;
let _parens: syn::token::Paren = parenthesized!(content in input);
let f: Punctuated<Field, Token![,]> = content.parse_terminated(Field::parse, Token![,])?;
fields = f.into_iter().collect();
},
_ => return Err(syn::Error::new(ident.span(), "Unknown macro command, expected `fields`")),
}
}
Ok(Self {
type_path,
generics: syn::Generics::default(),
fields,
})
}
}
fn gen_match_field_name_arm(simple: &SimpleStruct, default: &proc_macro2::TokenStream, arm_gen: fn(field: &Field) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream {
let field_arms_iter = simple.fields.iter().map(|f| {
let fname = &f.name;
let arm = arm_gen(f);
quote! {
stringify!(#fname) => #arm
}
});
quote! {
match name {
#(#field_arms_iter,)*
_ => #default,
}
}
}
fn gen_match_field_index_arm(simple: &SimpleStruct, default: &proc_macro2::TokenStream, arm_gen: fn(field: &Field) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream {
let field_arms_iter = simple.fields.iter().enumerate().map(|(idx, f)| {
let arm = arm_gen(f);
quote! {
#idx => #arm
}
});
quote! {
match idx {
#(#field_arms_iter,)*
_ => #default,
}
}
}
pub(crate) fn impl_reflect_simple_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let simple = syn::parse_macro_input!(input as SimpleStruct);
let type_path = &simple.type_path;
let (impl_generics, ty_generics, where_clause) = simple.generics.split_for_impl();
// convert the type path to a string. This would not create a leading separator
/* let type_path_str = {
let idents: Vec<String> = type_path.segments.iter()
.map(|segment| segment.ident.to_string())
.collect();
idents.join("::")
}; */
let borrow_fn = |field: &Field| {
let name = &field.name;
quote! {
Some(&self.#name)
}
};
let borrow_mut_fn = |field: &Field| {
let name = &field.name;
quote! {
Some(&mut self.#name)
}
};
let none_default = quote!(None);
let false_return_default = quote!( { return false; });
let field_count = simple.fields.len();
let field_fn = gen_match_field_name_arm(&simple, &none_default, borrow_fn);
let field_mut_fn = gen_match_field_name_arm(&simple, &none_default, borrow_mut_fn);
let field_at_fn = gen_match_field_index_arm(&simple, &none_default, borrow_fn);
let field_at_mut_fn = gen_match_field_index_arm(&simple, &none_default, borrow_mut_fn);
let field_name_at_fn = gen_match_field_index_arm(&simple, &none_default, |f| {
let name = &f.name;
quote! {
Some(stringify!(#name))
}
});
let set_field_arm = |f: &Field| {
let name = &f.name;
let ty = &f.ty;
quote! {
self.#name = any_val.downcast_ref::<#ty>()
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", stringify!(#name), val.name()))
.clone() //Some(&#mut_tkn self.#field_ident)
}
};
let set_field_fn = gen_match_field_name_arm(&simple, &false_return_default, set_field_arm);
let set_field_at_fn = gen_match_field_index_arm(&simple, &false_return_default, set_field_arm);
let get_field_ty_arm = |f: &Field| {
let fty = &f.ty;
quote! {
Some(stringify!(#fty))
}
};
let field_type_fn = gen_match_field_name_arm(&simple, &none_default, get_field_ty_arm);
let field_type_at_fn = gen_match_field_index_arm(&simple, &none_default, get_field_ty_arm);
proc_macro::TokenStream::from(quote! {
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
fn name(&self) -> ::std::string::String {
stringify!(#type_path).to_string()
}
fn type_id(&self) -> std::any::TypeId {
std::any::TypeId::of::<#type_path #ty_generics>()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) {
let val = val.as_any().downcast_ref::<Self>()
.expect("The type of `val` is not the same as `self`");
*self = val.clone();
}
fn clone_inner(&self) -> Box<dyn lyra_engine::reflect::Reflect> {
Box::new(self.clone())
}
fn reflect_ref(&self) -> lyra_engine::reflect::ReflectRef {
lyra_engine::reflect::ReflectRef::Value(self)
}
fn reflect_mut(&mut self) -> lyra_engine::reflect::ReflectMut {
lyra_engine::reflect::ReflectMut::Value(self)
}
fn reflect_val(&self) -> &dyn lyra_engine::reflect::Reflect {
self
}
fn reflect_val_mut(&mut self) -> &mut dyn lyra_engine::reflect::Reflect {
self
}
}
impl #impl_generics lyra_engine::reflect::Struct for #type_path #ty_generics #where_clause {
fn field(&self, name: &str) -> Option<&dyn Reflect> {
#field_fn
}
fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> {
#field_mut_fn
}
fn fields_len(&self) -> usize {
#field_count
}
fn field_at(&self, idx: usize) -> Option<&dyn Reflect> {
#field_at_fn
}
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> {
#field_at_mut_fn
}
fn field_name_at(&self, idx: usize) -> Option<&str> {
#field_name_at_fn
}
fn set_field(&mut self, name: &str, val: &dyn Reflect) -> bool {
let any_val = val.as_any();
#set_field_fn
true
}
fn set_field_at(&mut self, idx: usize, val: &dyn Reflect) -> bool {
let any_val = val.as_any();
#set_field_at_fn
true
}
fn field_type(&self, name: &str) -> Option<&'static str> {
#field_type_fn
}
fn field_type_at(&self, idx: usize) -> Option<&'static str> {
#field_type_at_fn
}
fn method(&self, name: &str) -> Option<&Method> {
unimplemented!()
}
fn method_at(&self, idx: usize) -> Option<&Method> {
unimplemented!()
}
fn methods_len(&self) -> usize {
unimplemented!()
}
}
})
}