Create an early scripting engine #2
|
@ -5,6 +5,10 @@ edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
math = ["dep:glam"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-reflect-derive = { path = "lyra-reflect-derive" }
|
lyra-reflect-derive = { path = "lyra-reflect-derive" }
|
||||||
lyra-ecs = { path = "../lyra-ecs" }
|
lyra-ecs = { path = "../lyra-ecs" }
|
||||||
|
glam = { version = "0.24.0", optional = true }
|
|
@ -11,6 +11,52 @@ mod struct_derive;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use struct_derive::*;
|
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)]
|
#[allow(dead_code)]
|
||||||
pub(crate) struct ReflectDef {
|
pub(crate) struct ReflectDef {
|
||||||
//pub ident: Ident,
|
//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 {
|
pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
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
|
generics
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn impl_reflect_simple_struct(input: TokenStream) -> TokenStream {
|
||||||
|
struct_macro::impl_reflect_simple_struct(input)
|
||||||
}
|
}
|
|
@ -1,7 +1,28 @@
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{DeriveInput, parse_quote, DataStruct};
|
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
|
/// 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.
|
/// 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,
|
/// _ => 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 {
|
fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
|
||||||
let mut_tkn = if is_mut {
|
let ty = StructType::new(data);
|
||||||
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)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
if ty == StructType::Named {
|
||||||
match name {
|
let mut_tkn = if is_mut {
|
||||||
#(#field_arms,)*
|
quote! {
|
||||||
_ => None,
|
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
|
/// 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 {
|
fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
let field_arms = data.fields.iter().map(|field| {
|
let ty = StructType::new(data);
|
||||||
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)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
if ty == StructType::Named {
|
||||||
let any_val = val.as_any();
|
let field_arms = data.fields.iter().map(|field| {
|
||||||
match name {
|
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
||||||
#(#field_arms,)*
|
let field_name_str = field_ident.to_string();
|
||||||
_ => {
|
let field_ty = &field.ty;
|
||||||
return false;
|
|
||||||
},
|
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
|
/// 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 {
|
fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
let field_arms = data.fields.iter().map(|field| {
|
let ty = StructType::new(data);
|
||||||
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
|
||||||
let field_name_str = field_ident.to_string();
|
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! {
|
quote! {
|
||||||
#field_name_str => Some(#s)
|
match name {
|
||||||
|
#(#field_arms,)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
} else { quote!(None) }
|
||||||
|
|
||||||
quote! {
|
|
||||||
match name {
|
|
||||||
#(#field_arms,)*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates code that matches a string with a struct's field name, and sets that field value
|
/// 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 {
|
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_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;
|
let field_ty = &field.ty;
|
||||||
|
|
||||||
quote! {
|
let attrs = FieldAttributes::from_vec(&field.attrs)
|
||||||
#idx => self.#field_ident = any_val.downcast_ref::<#field_ty>()
|
.expect("Failure to parse reflect attributes");
|
||||||
.expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name()))
|
if attrs.has_skip() {
|
||||||
.clone()
|
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;
|
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 {
|
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 {
|
let mut_tkn = if is_mut {
|
||||||
quote! {
|
quote! {
|
||||||
mut
|
mut
|
||||||
|
@ -219,10 +303,23 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T
|
||||||
} else { quote!{} };
|
} else { quote!{} };
|
||||||
|
|
||||||
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
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! {
|
if let Some(field_ident) = &field.ident {
|
||||||
#idx => Some(&#mut_tkn self.#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 {
|
fn gen_struct_field_name_idx(data: &DataStruct) -> proc_macro2::TokenStream {
|
||||||
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
let ty = StructType::new(data);
|
||||||
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! {
|
if ty == StructType::Named {
|
||||||
match idx {
|
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
|
||||||
#(#field_arms,)*
|
let field_ident = field.ident.as_ref().expect("Struct is missing field ident!");
|
||||||
_ => None,
|
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 {
|
fn set_field(&mut self, name: &str, val: &dyn lyra_engine::reflect::Reflect) -> bool {
|
||||||
#set_field_named
|
#set_field_named
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_field_at(&mut self, idx: usize, val: &dyn lyra_engine::reflect::Reflect) -> bool {
|
fn set_field_at(&mut self, idx: usize, val: &dyn lyra_engine::reflect::Reflect) -> bool {
|
||||||
#set_field_idx
|
#set_field_idx
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field_type(&self, name: &str) -> Option<&'static str> {
|
fn field_type(&self, name: &str) -> Option<&'static str> {
|
||||||
|
|
|
@ -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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -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);
|
|
@ -4,7 +4,7 @@ use crate::List;
|
||||||
|
|
||||||
use crate::lyra_engine;
|
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!(bool);
|
||||||
impl_reflect_trait_value!(char);
|
impl_reflect_trait_value!(char);
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod impl_std;
|
||||||
|
|
||||||
|
#[cfg(feature="math")]
|
||||||
|
pub mod impl_math;
|
|
@ -6,6 +6,8 @@ use lyra_ecs::{world::World, DynamicBundle, Component, Entity, ComponentInfo};
|
||||||
|
|
||||||
extern crate self as lyra_reflect;
|
extern crate self as lyra_reflect;
|
||||||
|
|
||||||
|
pub use lyra_reflect_derive::*;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub(crate) mod lyra_engine {
|
pub(crate) mod lyra_engine {
|
||||||
pub(crate) mod reflect {
|
pub(crate) mod reflect {
|
||||||
|
@ -42,7 +44,7 @@ pub use method::*;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub use registry::*;
|
pub use registry::*;
|
||||||
|
|
||||||
pub mod impl_std;
|
pub mod impls;
|
||||||
|
|
||||||
pub trait Reflect: Any {
|
pub trait Reflect: Any {
|
||||||
fn name(&self) -> String;
|
fn name(&self) -> String;
|
||||||
|
|
|
@ -52,7 +52,6 @@ pub trait Enum: Reflect {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use lyra_reflect_derive::Reflect;
|
|
||||||
|
|
||||||
use super::EnumType;
|
use super::EnumType;
|
||||||
use crate::{lyra_engine, Reflect, ReflectRef};
|
use crate::{lyra_engine, Reflect, ReflectRef};
|
||||||
|
|
|
@ -41,7 +41,6 @@ pub trait Struct: Reflect {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use lyra_reflect_derive::Reflect;
|
|
||||||
use crate::{Reflect, ReflectRef, ReflectMut};
|
use crate::{Reflect, ReflectRef, ReflectMut};
|
||||||
use crate::lyra_engine;
|
use crate::lyra_engine;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,12 @@ impl TypeRegistry {
|
||||||
self.inner.get_mut(&type_id)
|
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)
|
pub fn register_type<T>(&mut self)
|
||||||
where
|
where
|
||||||
T: AsRegisteredType + 'static
|
T: AsRegisteredType + 'static
|
||||||
|
|
Loading…
Reference in New Issue