diff --git a/lyra-reflect/Cargo.toml b/lyra-reflect/Cargo.toml index db57f34..89643d2 100644 --- a/lyra-reflect/Cargo.toml +++ b/lyra-reflect/Cargo.toml @@ -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" } \ No newline at end of file +lyra-ecs = { path = "../lyra-ecs" } +glam = { version = "0.24.0", optional = true } \ No newline at end of file diff --git a/lyra-reflect/lyra-reflect-derive/src/lib.rs b/lyra-reflect/lyra-reflect-derive/src/lib.rs index 2d5b1a3..a8b8ff0 100644 --- a/lyra-reflect/lyra-reflect-derive/src/lib.rs +++ b/lyra-reflect/lyra-reflect-derive/src/lib.rs @@ -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); + +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) -> Result { + let s: Result, _> = v.iter().filter_map(|att| match &att.meta { + syn::Meta::Path(_) => None, + syn::Meta::List(l) => { + Some(syn::parse::(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 { + 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 TokenStream { + struct_macro::impl_reflect_simple_struct(input) } \ No newline at end of file diff --git a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs index 1661e16..1f54547 100644 --- a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs +++ b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs @@ -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> { diff --git a/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs b/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs new file mode 100644 index 0000000..d1c9b5a --- /dev/null +++ b/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs @@ -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 { + 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!() + } + } + }) +} diff --git a/lyra-reflect/src/impls/impl_math.rs b/lyra-reflect/src/impls/impl_math.rs new file mode 100644 index 0000000..27a3e0f --- /dev/null +++ b/lyra-reflect/src/impls/impl_math.rs @@ -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); \ No newline at end of file diff --git a/lyra-reflect/src/impl_std.rs b/lyra-reflect/src/impls/impl_std.rs similarity index 98% rename from lyra-reflect/src/impl_std.rs rename to lyra-reflect/src/impls/impl_std.rs index d756f89..9c135e0 100644 --- a/lyra-reflect/src/impl_std.rs +++ b/lyra-reflect/src/impls/impl_std.rs @@ -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); diff --git a/lyra-reflect/src/impls/mod.rs b/lyra-reflect/src/impls/mod.rs new file mode 100644 index 0000000..64af2fb --- /dev/null +++ b/lyra-reflect/src/impls/mod.rs @@ -0,0 +1,4 @@ +pub mod impl_std; + +#[cfg(feature="math")] +pub mod impl_math; \ No newline at end of file diff --git a/lyra-reflect/src/lib.rs b/lyra-reflect/src/lib.rs index ae5c233..2b6bd24 100644 --- a/lyra-reflect/src/lib.rs +++ b/lyra-reflect/src/lib.rs @@ -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; diff --git a/lyra-reflect/src/reflected_enum.rs b/lyra-reflect/src/reflected_enum.rs index abbdaf9..ebfed11 100644 --- a/lyra-reflect/src/reflected_enum.rs +++ b/lyra-reflect/src/reflected_enum.rs @@ -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}; diff --git a/lyra-reflect/src/reflected_struct.rs b/lyra-reflect/src/reflected_struct.rs index ce3ec1a..72904ca 100644 --- a/lyra-reflect/src/reflected_struct.rs +++ b/lyra-reflect/src/reflected_struct.rs @@ -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; diff --git a/lyra-reflect/src/registry.rs b/lyra-reflect/src/registry.rs index 0311f10..46651f4 100644 --- a/lyra-reflect/src/registry.rs +++ b/lyra-reflect/src/registry.rs @@ -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(&mut self) where T: AsRegisteredType + 'static