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 { Ok(Self { name: input.parse()?, _eq: input.parse()?, ty: input.parse()?, }) } } struct SimpleStruct { type_path: syn::Path, pub generics: syn::Generics, fields: Vec, } impl syn::parse::Parse for SimpleStruct { fn parse(input: syn::parse::ParseStream) -> syn::Result { //let type_path = syn::Path::parse_mod_style(input)?; let type_path: syn::Path = input.parse()?; /* let mut generics = input.parse::()?; 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 = 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 = 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::() .expect("The type of `val` is not the same as `self`"); *self = val.clone(); } fn clone_inner(&self) -> Box { 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!() } } }) }