reflect: Implement reflect for glam types

This commit is contained in:
SeanOMik 2024-01-12 14:08:46 -05:00
parent eb44aba3dc
commit 544aee4a31
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
11 changed files with 531 additions and 85 deletions

View File

@ -5,6 +5,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
math = ["dep:glam"]
[dependencies]
lyra-reflect-derive = { path = "lyra-reflect-derive" }
lyra-ecs = { path = "../lyra-ecs" }
lyra-ecs = { path = "../lyra-ecs" }
glam = { version = "0.24.0", optional = true }

View File

@ -11,6 +11,52 @@ mod struct_derive;
#[allow(unused_imports)]
use struct_derive::*;
mod struct_macro;
/* #[proc_macro_attribute(attributes(reflect))]
pub fn reflect(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
item
} */
pub(crate) struct FieldAttributes(Vec<ReflectAttribute>);
impl FieldAttributes {
/// Searches for a usage of the 'reflect' attribute and returns a list of the
/// things used in the usage.
pub fn from_vec(v: &Vec<syn::Attribute>) -> Result<Self, syn::Error> {
let s: Result<Vec<ReflectAttribute>, _> = v.iter().filter_map(|att| match &att.meta {
syn::Meta::Path(_) => None,
syn::Meta::List(l) => {
Some(syn::parse::<ReflectAttribute>(l.tokens.clone().into()))
}
syn::Meta::NameValue(_) => None
}).collect();
Ok(Self(s?))
}
pub fn has_skip(&self) -> bool {
self.0.iter().any(|a| *a == ReflectAttribute::Skip)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ReflectAttribute {
Skip
}
impl syn::parse::Parse for ReflectAttribute {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident: Ident = input.parse()?;
let ident_str = ident.to_string().to_lowercase();
match ident_str.as_str() {
"skip" => Ok(Self::Skip),
_ => Err(syn::Error::new(ident.span(), "Unknown reflect attribute flag"))
}
}
}
#[allow(dead_code)]
pub(crate) struct ReflectDef {
//pub ident: Ident,
@ -36,7 +82,7 @@ impl syn::parse::Parse for ReflectDef {
}
}
#[proc_macro_derive(Reflect)]
#[proc_macro_derive(Reflect, attributes(reflect))]
pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
@ -127,4 +173,9 @@ pub(crate) fn add_trait_bounds(mut generics: Generics, add_bounds: Vec<TypeParam
}
}
generics
}
#[proc_macro]
pub fn impl_reflect_simple_struct(input: TokenStream) -> TokenStream {
struct_macro::impl_reflect_simple_struct(input)
}

View File

@ -1,7 +1,28 @@
use quote::{quote, ToTokens};
use syn::{DeriveInput, parse_quote, DataStruct};
use crate::add_trait_bounds;
use crate::{add_trait_bounds, FieldAttributes};
#[derive(Debug, PartialEq, Eq)]
enum StructType {
Unit,
Named,
Tuple,
}
impl StructType {
pub fn new(data: &DataStruct) -> Self {
if let Some(first) = data.fields.iter().next() {
if first.ident.is_some() {
Self::Named
} else {
Self::Tuple
}
} else {
Self::Unit
}
}
}
/// Generates code that matches a string with a struct's field name, and returns an Option that
/// contains a borrow (mutable borrow if `is_mut` is true) to the matching struct field.
@ -22,28 +43,42 @@ use crate::add_trait_bounds;
/// _ => None,
/// }
/// ```
///
/// If the struct is a unit or tuple struct, None will always be returned
fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
let mut_tkn = if is_mut {
quote! {
mut
}
} else { quote!{} };
let field_arms = data.fields.iter().map(|field| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
quote! {
#field_name_str => Some(&#mut_tkn self.#field_ident)
}
});
let ty = StructType::new(data);
quote! {
match name {
#(#field_arms,)*
_ => None,
if ty == StructType::Named {
let mut_tkn = if is_mut {
quote! {
mut
}
} else { quote!{} };
let field_arms = data.fields.iter().map(|field| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
let attrs = FieldAttributes::from_vec(&field.attrs)
.expect("Failure to parse reflect attributes");
if attrs.has_skip() {
quote! {
#field_name_str => None
}
} else {
quote! {
#field_name_str => Some(&#mut_tkn self.#field_ident)
}
}
});
quote! {
match name {
#(#field_arms,)*
_ => None,
}
}
}
} else { quote!(None) }
}
/// Generates code that matches a string with a struct's field name, and sets that field value
@ -64,27 +99,41 @@ fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::Token
/// }
/// ```
fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream {
let field_arms = data.fields.iter().map(|field| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
let field_ty = &field.ty;
quote! {
#field_name_str => self.#field_ident = any_val.downcast_ref::<#field_ty>()
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name()))
.clone() //Some(&#mut_tkn self.#field_ident)
}
});
let ty = StructType::new(data);
quote! {
let any_val = val.as_any();
match name {
#(#field_arms,)*
_ => {
return false;
},
if ty == StructType::Named {
let field_arms = data.fields.iter().map(|field| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
let field_ty = &field.ty;
let attrs = FieldAttributes::from_vec(&field.attrs)
.expect("Failure to parse reflect attributes");
if attrs.has_skip() {
quote! {
#field_name_str => {}
}
} else {
quote! {
#field_name_str => self.#field_ident = any_val.downcast_ref::<#field_ty>()
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name()))
.clone() //Some(&#mut_tkn self.#field_ident)
}
}
});
quote! {
let any_val = val.as_any();
match name {
#(#field_arms,)*
_ => {
return false;
},
}
true
}
}
} else { quote!(return false;) }
}
/// Generates code that matches a string with a struct's field name, and returns a string that is
@ -99,25 +148,29 @@ fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream {
/// }
/// ```
fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream {
let field_arms = data.fields.iter().map(|field| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
let ty = StructType::new(data);
if ty == StructType::Named {
let field_arms = data.fields.iter().map(|field| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
let mut field_ty_stream = proc_macro2::TokenStream::new();
field.ty.to_tokens(&mut field_ty_stream);
let s = field_ty_stream.to_string();
quote! {
#field_name_str => Some(#s)
}
});
let mut field_ty_stream = proc_macro2::TokenStream::new();
field.ty.to_tokens(&mut field_ty_stream);
let s = field_ty_stream.to_string();
quote! {
#field_name_str => Some(#s)
match name {
#(#field_arms,)*
_ => None,
}
}
});
quote! {
match name {
#(#field_arms,)*
_ => None,
}
}
} else { quote!(None) }
}
/// Generates code that matches a string with a struct's field name, and sets that field value
@ -138,15 +191,38 @@ fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream {
/// }
/// ```
fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream {
let ty = StructType::new(data);
if ty == StructType::Unit {
return quote!( return false; );
}
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
let field_ty = &field.ty;
quote! {
#idx => self.#field_ident = any_val.downcast_ref::<#field_ty>()
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name()))
.clone()
let attrs = FieldAttributes::from_vec(&field.attrs)
.expect("Failure to parse reflect attributes");
if attrs.has_skip() {
return quote! {
#idx => {}
};
}
if let Some(field_ident) = &field.ident {
let field_name_str = field_ident.to_string();
quote! {
#idx => self.#field_ident = any_val.downcast_ref::<#field_ty>()
.expect(&format!("Cannot set struct's field of {} to the provided type of {}", #field_name_str, val.name()))
.clone()
}
} else {
let sidx = syn::Index::from(idx);
quote! {
#idx => self.#sidx = any_val.downcast_ref::<#field_ty>()
.expect(&format!("Cannot set struct's field at {} to the provided type of {}", #idx, val.name()))
.clone()
}
}
});
@ -158,6 +234,8 @@ fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream
return false;
},
}
true
}
}
@ -212,6 +290,12 @@ fn gen_struct_field_name_match_idx(data: &DataStruct) -> proc_macro2::TokenStrea
/// }
/// ```
fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
let ty = StructType::new(data);
if ty == StructType::Unit {
return quote!(None);
}
let mut_tkn = if is_mut {
quote! {
mut
@ -219,10 +303,23 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T
} else { quote!{} };
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let attrs = FieldAttributes::from_vec(&field.attrs)
.expect("Failure to parse reflect attributes");
if attrs.has_skip() {
return quote! {
#idx => None
};
}
quote! {
#idx => Some(&#mut_tkn self.#field_ident)
if let Some(field_ident) = &field.ident {
quote! {
#idx => Some(&#mut_tkn self.#field_ident)
}
} else {
let sidx = syn::Index::from(idx);
quote! {
#idx => Some(&#mut_tkn self.#sidx)
}
}
});
@ -246,20 +343,26 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T
/// }
/// ```
fn gen_struct_field_name_idx(data: &DataStruct) -> proc_macro2::TokenStream {
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
quote! {
#idx => Some(#field_name_str)
}
});
let ty = StructType::new(data);
quote! {
match idx {
#(#field_arms,)*
_ => None,
if ty == StructType::Named {
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
let field_name_str = field_ident.to_string();
quote! {
#idx => Some(#field_name_str)
}
});
quote! {
match idx {
#(#field_arms,)*
_ => None,
}
}
} else {
quote!(None)
}
}
@ -361,12 +464,10 @@ pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> p
fn set_field(&mut self, name: &str, val: &dyn lyra_engine::reflect::Reflect) -> bool {
#set_field_named
true
}
fn set_field_at(&mut self, idx: usize, val: &dyn lyra_engine::reflect::Reflect) -> bool {
#set_field_idx
true
}
fn field_type(&self, name: &str) -> Option<&'static str> {

View File

@ -0,0 +1,270 @@
use quote::quote;
use syn::{Token, parenthesized, punctuated::Punctuated};
struct Field {
name: syn::Ident,
_eq: Token![=],
ty: syn::Ident,
}
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!()
}
}
})
}

View File

@ -0,0 +1,10 @@
use lyra_reflect_derive::{impl_reflect_simple_struct, impl_reflect_trait_value};
use crate::{lyra_engine, Reflect, Method};
impl_reflect_simple_struct!(glam::Vec2, fields(x=f32, y=f32));
impl_reflect_simple_struct!(glam::Vec3, fields(x=f32, y=f32, z=f32));
impl_reflect_simple_struct!(glam::Vec4, fields(x=f32, y=f32, z=f32, w=f32));
impl_reflect_simple_struct!(glam::Quat, fields(x=f32, y=f32, z=f32, w=f32));
impl_reflect_trait_value!(glam::Mat4);

View File

@ -4,7 +4,7 @@ use crate::List;
use crate::lyra_engine;
use super::{Reflect, ReflectRef, ReflectMut, util};
use crate::{Reflect, ReflectRef, ReflectMut, util};
impl_reflect_trait_value!(bool);
impl_reflect_trait_value!(char);

View File

@ -0,0 +1,4 @@
pub mod impl_std;
#[cfg(feature="math")]
pub mod impl_math;

View File

@ -6,6 +6,8 @@ use lyra_ecs::{world::World, DynamicBundle, Component, Entity, ComponentInfo};
extern crate self as lyra_reflect;
pub use lyra_reflect_derive::*;
#[allow(unused_imports)]
pub(crate) mod lyra_engine {
pub(crate) mod reflect {
@ -42,7 +44,7 @@ pub use method::*;
pub mod registry;
pub use registry::*;
pub mod impl_std;
pub mod impls;
pub trait Reflect: Any {
fn name(&self) -> String;

View File

@ -52,7 +52,6 @@ pub trait Enum: Reflect {
#[allow(unused_variables)]
#[cfg(test)]
mod tests {
use lyra_reflect_derive::Reflect;
use super::EnumType;
use crate::{lyra_engine, Reflect, ReflectRef};

View File

@ -41,7 +41,6 @@ pub trait Struct: Reflect {
#[cfg(test)]
mod tests {
use lyra_reflect_derive::Reflect;
use crate::{Reflect, ReflectRef, ReflectMut};
use crate::lyra_engine;

View File

@ -21,6 +21,12 @@ impl TypeRegistry {
self.inner.get_mut(&type_id)
}
/// Get a registered type, or register a new type and return it.
pub fn get_type_or_default(&mut self, type_id: TypeId) -> &mut RegisteredType {
self.inner.entry(type_id)
.or_insert(RegisteredType::default())
}
pub fn register_type<T>(&mut self)
where
T: AsRegisteredType + 'static