Add lyra-reflect
ci/woodpecker/push/debug Pipeline failed Details

This commit is contained in:
SeanOMik 2023-12-30 18:55:05 -05:00
parent d075fd5d5f
commit 52e58b1ca5
20 changed files with 2646 additions and 1 deletions

18
Cargo.lock generated
View File

@ -1298,6 +1298,7 @@ dependencies = [
"instant",
"itertools",
"lyra-ecs",
"lyra-reflect",
"lyra-resource",
"quote",
"syn 2.0.42",
@ -1310,6 +1311,23 @@ dependencies = [
"winit",
]
[[package]]
name = "lyra-reflect"
version = "0.1.0"
dependencies = [
"lyra-ecs",
"lyra-reflect-derive",
]
[[package]]
name = "lyra-reflect-derive"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.42",
]
[[package]]
name = "lyra-resource"
version = "0.0.1"

View File

@ -8,11 +8,12 @@ members = [
"lyra-resource",
"lyra-ecs",
"examples/testbed"
]
, "lyra-reflect"]
[dependencies]
lyra-resource = { path = "lyra-resource", version = "0.0.1" }
lyra-ecs = { path = "lyra-ecs" }
lyra-reflect = { path = "lyra-reflect" }
winit = "0.28.1"
tracing = "0.1.37"

10
lyra-reflect/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "lyra-reflect"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lyra-reflect-derive = { path = "lyra-reflect-derive" }
lyra-ecs = { path = "../lyra-ecs" }

View File

@ -0,0 +1,14 @@
[package]
name = "lyra-reflect-derive"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.70"
quote = "1.0.33"
syn = "2.0.41"

View File

@ -0,0 +1,607 @@
use proc_macro2::Ident;
use quote::quote;
use syn::{token::Enum, DataEnum, DeriveInput, Generics, GenericParam, parse_quote, Variant};
use crate::add_trait_bounds;
static ASCII_LOWER: [char; 26] = [
'a', 'b', 'c', 'd', 'e',
'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y',
'z',
];
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
enum VariantType {
Unit,
Struct,
Tuple,
}
impl From<&Variant> for VariantType {
fn from(value: &Variant) -> Self {
match value.fields {
syn::Fields::Named(_) => VariantType::Struct,
syn::Fields::Unnamed(_) => VariantType::Tuple,
syn::Fields::Unit => VariantType::Unit,
}
}
}
/// Generates the following different outputs:
///
/// ```rust
/// // for struct variants
/// TestEnum::Error { msg, code }
///
/// // for tuple variants
/// TestEnum::Middle(a, b)
///
/// // for unit variants
/// TestEnum::Start
/// ```
fn gen_variant_full_id(enum_id: &proc_macro2::Ident, variant: &Variant) -> proc_macro2::TokenStream {
let var_ty = VariantType::from(variant);
let var_id = &variant.ident;
let fields = variant.fields.iter().enumerate().map(|(idx, field)| {
match var_ty {
VariantType::Unit => {
return quote! { };
},
VariantType::Struct => {
let id = field.ident.as_ref().unwrap();
return quote! { #id }
},
VariantType::Tuple => {
let id = Ident::new(ASCII_LOWER[idx].to_string().as_str(),
proc_macro2::Span::call_site());
return quote! { #id }
}
}
});
let fields = match var_ty {
VariantType::Unit => quote! {},
VariantType::Struct => {
quote!{ { #( #fields ),* } }
},
VariantType::Tuple => {
quote!{ ( #( #fields ),* ) }
},
};
quote! {
#enum_id::#var_id #fields
}
}
/// Generates an if statement to check if `self` is `variant`. The body of the if statement is replaced by `if_body`
fn gen_variant_if(enum_id: &proc_macro2::Ident, variant: &Variant, if_body: proc_macro2::TokenStream, prepend_else: bool) -> proc_macro2::TokenStream {
let variant_check = gen_variant_full_id(enum_id, variant);
let optional_else = if prepend_else {
quote! { else }
} else {
quote! {}
};
quote! {
#optional_else if let #variant_check = self {
#if_body
}
}
}
/// Generates the following:
///
/// ```rust
/// /// generated one field here
/// if name == "msg" {
/// return Some(msg);
/// }
///
/// /// another field here
/// if name == "code" {
/// return Some(code);
/// }
/// ```
/// Continues to generate if statements until the variant runs out of fields.
fn gen_if_field_names(variant: &Variant) -> proc_macro2::TokenStream {
let field_ifs = variant.fields.iter().map(|field| {
let id = field.ident.as_ref().unwrap();
let id_str = id.span().source_text().unwrap();
quote! {
if name == #id_str {
return Some(#id);
}
}
});
quote! {
#( #field_ifs )*
}
}
/// Generates the following rust code:
///
/// ```rust
/// match name {
/// "msg" | "code" => true,
/// _ => false,
/// }
/// ```
/// More strings may be added to the true match arm depending on the enum struct variant fields
fn gen_match_names(variant: &Variant) -> proc_macro2::TokenStream {
let field_name_strs = variant.fields.iter().map(|field| {
let id = field.ident.as_ref()
.expect("Could not find identifier for enum field!");
id.span().source_text().unwrap()
});
quote! {
return match name {
#( #field_name_strs )|* => true,
_ => false,
};
}
}
/// Generates the following:
///
/// ```rust
/// /// generated one field here
/// if idx == 0 {
/// return Some(a);
/// }
///
/// /// another field here
/// if idx == 1 {
/// return Some(b);
/// }
/// ```
/// Continues to generate if statements until the variant runs out of fields.
fn gen_if_field_indices(variant: &Variant) -> proc_macro2::TokenStream {
let vty = VariantType::from(variant);
let field_ifs = variant.fields.iter().enumerate()
.map(|(idx, field)| {
let id = if vty == VariantType::Tuple {
Ident::new(ASCII_LOWER[idx].to_string().as_str(),
proc_macro2::Span::call_site())
} else {
field.ident.clone().unwrap()
};
quote! {
if idx == #idx {
return Some(#id);
}
}
});
quote! {
#( #field_ifs )*
}
}
/// Generates the following:
///
/// ```rust
/// /// generated one field here
/// if idx == 0 {
/// return Some("a");
/// }
///
/// /// another field here
/// if idx == 1 {
/// return Some("b");
/// }
/// ```
/// Continues to generate if statements until the variant runs out of fields.
fn gen_if_field_indices_names(variant: &Variant) -> proc_macro2::TokenStream {
let vty = VariantType::from(variant);
let field_ifs = variant.fields.iter().enumerate()
.map(|(idx, field)| {
let id_str = if vty == VariantType::Tuple {
ASCII_LOWER[idx].to_string()
} else {
field.ident.clone().unwrap().to_string()
};
quote! {
if idx == #idx {
return Some(#id_str.to_string());
}
}
});
quote! {
#( #field_ifs )*
}
}
/// Generates the following:
/// ```rust
/// /// when `by_index` is false:
///
/// if let TestEnum::Error{ msg, code} = self {
/// if name == "msg" {
/// return Some(msg);
/// }
///
/// if name == "code" {
/// return Some(code);
/// }
/// }
///
/// /// when `by_index` is true:
///
/// if let TestEnum::Something(a, b) = self {
/// if idx == 0 {
/// return Some(a);
/// }
///
/// if idx == 1 {
/// return Some(b);
/// }
/// }
///
/// if let TestEnum::Error{ msg, code} = self {
/// if idx == 0 {
/// return Some(msg);
/// }
///
/// if idx == 1 {
/// return Some(code);
/// }
/// }
/// ```
/// And so on, for each variant that the enum provided has.
///
/// Parameters
/// * `by_index`: Should the if statements be generated to check indices.
fn gen_enum_if_stmts(enum_id: &proc_macro2::Ident, data: &DataEnum, by_index: bool) -> proc_macro2::TokenStream {
let mut if_statement_count = 0;
let struct_vars = data.variants.iter().enumerate().map(|(idx, var)| {
let vty = VariantType::from(var);
let prepend_else = if_statement_count > 0;
match vty {
VariantType::Struct => if by_index {
if_statement_count += 1;
let if_body = gen_if_field_indices(var);
gen_variant_if(enum_id, var, if_body, prepend_else)
} else {
if_statement_count += 1;
let if_body = gen_if_field_names(var);
gen_variant_if(enum_id, var, if_body, prepend_else)
},
VariantType::Tuple => if by_index {
if_statement_count += 1;
let if_body = gen_if_field_indices(var);
gen_variant_if(enum_id, var, if_body, prepend_else)
} else {
quote! { }
},
_ => quote! { },
}
});
println!("====");
quote! {
#( #struct_vars )*
}
}
/// Generates the following rust code:
///
/// ```rust
/// if let TestEnum::Error { msg, code } = self {
/// return match name {
/// // expands for continuing struct fields
/// "msg" | "code" => true,
/// _ => false,
/// };
/// }
/// ```
/// And so on until the enum runs out of struct variants.
fn gen_enum_has_field(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_macro2::TokenStream {
let struct_vars = data.variants.iter().map(|var| {
let vty = VariantType::from(var);
match vty {
VariantType::Struct => {
let match_name = gen_match_names(var);
gen_variant_if(enum_id, var, match_name, false)
},
_ => quote! { },
}
});
quote! {
#( #struct_vars )*
}
}
/// Generates the following code:
///
/// ```rust
/// match self {
/// TestEnum::Start => 0,
/// TestEnum::Middle(a, b) => 2,
/// TestEnum::Error { msg, code } => 2,
/// }
/// ```
/// and so on for each variant of the enum
fn gen_enum_fields_len(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_macro2::TokenStream {
let variant_arms = data.variants.iter().map(|var| {
let variant_ident = gen_variant_full_id(enum_id, var);
let field_len = var.fields.len();
quote! {
#variant_ident => #field_len
}
});
quote! {
match self {
#( #variant_arms ),*
}
}
}
/// Generates the following code:
///
/// ```rust
/// if let TestEnum::Error { msg, code } = self {
/// if idx == 0 {
/// return Some("msg");
/// }
/// if idx == 1 {
/// return Some("code");
/// }
/// }
/// ```
/// and so on for each struct variant of the enum. The inner if statements expand for each
/// field of the variant.
fn gen_enum_field_name_at(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_macro2::TokenStream {
let variant_ifs = data.variants.iter().map(|var| {
let vty = VariantType::from(var);
match vty {
VariantType::Struct => {
let match_name = gen_if_field_indices_names(var);
gen_variant_if(enum_id, var, match_name, false)
},
_ => quote! { },
}
});
quote! {
#( #variant_ifs )*
}
}
/// Generates the following code:
/// ```rust
/// match self {
/// TestEnum::Start => 0,
/// TestEnum::Middle(a, b) => 1,
/// TestEnum::Error { msg, code } => 2,
/// }
/// ```
/// The match arms will expand for each variant the enum has.
fn gen_enum_variant_name(enum_id: &proc_macro2::Ident, data: &DataEnum, gen_index: bool) -> proc_macro2::TokenStream {
let variant_arms = data.variants.iter().enumerate().map(|(idx, var)| {
let variant_ident = gen_variant_full_id(enum_id, var);
let var_name = var.ident.to_string();
let arm_result = if gen_index {
quote! {
#idx
}
} else {
quote! {
#var_name.to_string()
}
};
quote! {
#variant_ident => #arm_result
}
});
quote! {
match self {
#( #variant_arms ),*
}
}
}
/// Generates a match statement that returns the types of the variants of the enum.
///
/// Example:
/// ```rust
/// match self {
/// TestEnum::Start => EnumType::Unit,
/// TestEnum::Middle(a, b) => EnumType::Tuple,
/// TestEnum::Error { msg, code } => EnumType::Struct,
/// }
/// ```
/// Match arms will be added for each variant of the enum.
fn gen_enum_variant_type(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_macro2::TokenStream {
let variant_arms = data.variants.iter().map(|var| {
let variant_ident = gen_variant_full_id(enum_id, var);
let vty = VariantType::from(var);
match vty {
VariantType::Struct => quote! { #variant_ident => EnumType::Struct },
VariantType::Tuple => quote! { #variant_ident => EnumType::Tuple },
VariantType::Unit => quote! { #variant_ident => EnumType::Unit },
}
});
quote! {
match self {
#( #variant_arms ),*
}
}
}
/// Create a reflect implementation for an enum
pub fn derive_reflect_enum(input: &DeriveInput, data_enum: &DataEnum) -> proc_macro2::TokenStream {
let type_path = &input.ident;
let name = type_path.span().source_text().unwrap();
//println!("Got type path: {}", type_path);
let variant_count = data_enum.variants.len();
/* let mut variants_iter = data_enum.variants.iter();
let variant = variants_iter.next().unwrap();
let variant_name = &variant.ident; */
let field_ifs = gen_enum_if_stmts(type_path, data_enum, false);
let field_mut_ifs = gen_enum_if_stmts(type_path, data_enum, false);
let field_at_ifs = gen_enum_if_stmts(type_path, data_enum, true);
let field_at_mut_ifs = gen_enum_if_stmts(type_path, data_enum, true);
let has_field = gen_enum_has_field(type_path, data_enum);
let field_len = gen_enum_fields_len(type_path, data_enum);
let field_name_at = gen_enum_field_name_at(type_path, data_enum);
let variant_name_match = gen_enum_variant_name(type_path, data_enum, false);
let variant_idx_match = gen_enum_variant_name(type_path, data_enum, true);
let variant_type = gen_enum_variant_type(type_path, data_enum);
let generics = add_trait_bounds(input.generics.clone(), vec![parse_quote!(Reflect), parse_quote!(Clone)]);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
return proc_macro2::TokenStream::from(quote! {
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
fn name(&self) -> ::std::string::String {
#name.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::Enum(self)
}
fn reflect_mut(&mut self) -> lyra_engine::reflect::ReflectMut {
lyra_engine::reflect::ReflectMut::Enum(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::Enum for #type_path #ty_generics #where_clause {
fn field(&self, name: &str) -> Option<&dyn lyra_engine::reflect::Reflect> {
let name = name.to_lowercase();
let name = name.as_str();
#field_ifs
None
}
fn field_mut(&mut self, name: &str) -> Option<&mut dyn lyra_engine::reflect::Reflect> {
let name = name.to_lowercase();
let name = name.as_str();
#field_mut_ifs
None
}
fn field_at(&self, idx: usize) -> Option<&dyn lyra_engine::reflect::Reflect> {
#field_at_ifs
None
}
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn lyra_engine::reflect::Reflect> {
#field_at_mut_ifs
None
}
fn has_field(&self, name: &str) -> bool {
let name = name.to_lowercase();
let name = name.as_str();
#has_field
false
}
fn fields_len(&self) -> usize {
#field_len
}
fn variants_len(&self) -> usize {
#variant_count
}
fn field_name_at(&self, idx: usize) -> Option<String> {
#field_name_at
None
}
fn variant_name(&self) -> String {
#variant_name_match
}
fn variant_index(&self) -> usize {
#variant_idx_match
}
fn variant_type(&self) -> lyra_engine::reflect::EnumType {
#variant_type
}
fn is_variant_name(&self, name: &str) -> bool {
let name = name.to_lowercase();
self.variant_name().to_lowercase() == name
}
}
})
}

View File

@ -0,0 +1,121 @@
use std::slice::Split;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{Generics, parse::ParseBuffer, Path, Attribute, GenericParam, parse_quote, parse_macro_input, DeriveInput, TypeParamBound};
mod enum_derive;
use enum_derive::*;
mod struct_derive;
use struct_derive::*;
pub(crate) struct ReflectDef {
pub type_path: Path,
pub generics: Generics,
pub attributes: Vec<Attribute>
}
impl syn::parse::Parse for ReflectDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let attributes = input.call(Attribute::parse_outer)?;
let type_path = Path::parse_mod_style(input)?;
let mut generics = input.parse::<Generics>()?;
generics.where_clause = input.parse()?;
Ok(Self {
type_path,
generics,
attributes,
})
}
}
#[proc_macro_derive(Reflect)]
pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let stream = match &input.data {
syn::Data::Enum(e) => {
enum_derive::derive_reflect_enum(&input, e)
},
syn::Data::Struct(s) => {
struct_derive::derive_reflect_struct(&input, s)
},
//syn::Data::Union(_) => todo!(),
_ => todo!()
};
proc_macro::TokenStream::from(stream)
}
/// Implements `Reflect` for Values. `ReflectRef` and `ReflectMut` will be the `Value`
/// variants, its used for simple types and primitives.
#[proc_macro]
pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
let reflect = syn::parse_macro_input!(input as ReflectDef);
let type_path = reflect.type_path.clone().into_token_stream();
let name_id = &reflect.type_path.segments.last().unwrap().ident;
let name = name_id.span().source_text().unwrap();
let (impl_generics, ty_generics, where_clause) = reflect.generics.split_for_impl();
TokenStream::from(quote! {
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
fn name(&self) -> ::std::string::String {
#name.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
}
}
})
}
pub(crate) fn add_trait_bounds(mut generics: Generics, add_bounds: Vec<TypeParamBound>) -> Generics {
for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param {
for bound in add_bounds.iter() {
type_param.bounds.push(bound.clone());
}
}
}
generics
}

View File

@ -0,0 +1,392 @@
use quote::{quote, ToTokens};
use syn::{DeriveInput, parse_quote, DataStruct};
use crate::add_trait_bounds;
/// 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.
///
/// Example:
/// ```rust
/// // when `is_mut` = false
/// match name {
/// "x" => Some(&self.x),
/// "y" => Some(&self.y),
/// _ => None,
/// }
///
/// // when `is_mut` = true
/// match name {
/// "x" => Some(&mut self.x),
/// "y" => Some(&mut self.y),
/// _ => None,
/// }
/// ```
fn gen_struct_field_match(struct_id: &proc_macro2::Ident, 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)
}
});
quote! {
match name {
#(#field_arms,)*
_ => None,
}
}
}
/// Generates code that matches a string with a struct's field name, and sets that field value
/// with the provided `val`.
///
/// Example:
/// ```rust
/// match name {
/// "x" => self.x = any_val.downcast_ref::<f32>()
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
/// .clone(),
/// "y" => self.y = any_val.downcast_ref::<f32>()
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
/// .clone()
/// _ => {
/// return false;
/// },
/// }
/// ```
fn gen_struct_set_field_match(struct_id: &proc_macro2::Ident, 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)
}
});
quote! {
let any_val = val.as_any();
match name {
#(#field_arms,)*
_ => {
return false;
},
}
}
}
/// Generates code that matches a string with a struct's field name, and returns a string that is
/// the type of the field.
///
/// Example:
/// ```rust
/// match name {
/// "x" => Some("f32"),
/// "y" => Some("f32"),
/// _ => None,
/// }
/// ```
fn gen_struct_field_name_match(struct_id: &proc_macro2::Ident, 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 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)
}
});
quote! {
match name {
#(#field_arms,)*
_ => None,
}
}
}
/// Generates code that matches a string with a struct's field name, and sets that field value
/// with the provided `val`.
///
/// Example:
/// ```rust
/// match name {
/// 0 => self.x = any_val.downcast_ref::<f32>()
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
/// .clone(),
/// 1 => self.y = any_val.downcast_ref::<f32>()
/// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name()))
/// .clone()
/// _ => {
/// return false;
/// },
/// }
/// ```
fn gen_struct_set_field_match_idx(struct_id: &proc_macro2::Ident, 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();
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()
}
});
quote! {
let any_val = val.as_any();
match idx {
#(#field_arms,)*
_ => {
return false;
},
}
}
}
/// Generates code that matches an index with the correct field in the struct, and returns the
/// type of the field.
///
/// Example:
/// ```rust
/// match name {
/// 0 => Some("f32"),
/// 1 => Some("f32"),
/// _ => None,
/// }
/// ```
fn gen_struct_field_name_match_idx(struct_id: &proc_macro2::Ident, data: &DataStruct) -> proc_macro2::TokenStream {
let field_arms = data.fields.iter().enumerate().map(|(idx, field)| {
let mut field_ty_stream = proc_macro2::TokenStream::new();
field.ty.to_tokens(&mut field_ty_stream);
let field_ty_str = field_ty_stream.to_string();
quote! {
#idx => Some(#field_ty_str)
}
});
quote! {
match idx {
#(#field_arms,)*
_ => None,
}
}
}
/// Generates code that matches an index with the position of the field in a struct,
/// and returns an Option that contains a borrow (mutable borrow if `is_mut` is true)
/// to the matching struct field.
///
/// Example:
/// ```rust
/// // when `is_mut` = false
/// match idx {
/// 0 => Some(&self.x),
/// 1 => Some(&self.y),
/// _ => None,
/// }
///
/// // when `is_mut` = true
/// match idx {
/// 0 => Some(&mut self.x),
/// 1 => Some(&mut self.y),
/// _ => None,
/// }
/// ```
fn gen_struct_field_match_idx(struct_id: &proc_macro2::Ident, data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream {
let mut_tkn = if is_mut {
quote! {
mut
}
} 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!");
quote! {
#idx => Some(&#mut_tkn self.#field_ident)
}
});
quote! {
match idx {
#(#field_arms,)*
_ => None,
}
}
}
/// Generates code that matches an index with the position of the field in a struct,
/// and returns an Option that contains the name of the field.
///
/// Example:
/// ```rust
/// match idx {
/// 0 => Some("x"),
/// 1 => Some("y"),
/// _ => None,
/// }
/// ```
fn gen_struct_field_name_idx(struct_id: &proc_macro2::Ident, 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)
}
});
quote! {
match idx {
#(#field_arms,)*
_ => None,
}
}
}
/// Generates a token stream that implements Reflect and Struct for the provided struct
pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> proc_macro2::TokenStream {
let type_path = &input.ident;
let name = type_path.span().source_text().unwrap();
let field_len = data_struct.fields.len();
let get_field_match = gen_struct_field_match(type_path, data_struct, false);
let get_field_mut_match = gen_struct_field_match(type_path, data_struct, true);
let get_field_match_idx = gen_struct_field_match_idx(type_path, data_struct, false);
let get_field_mut_match_idx = gen_struct_field_match_idx(type_path, data_struct, true);
let set_field_named = gen_struct_set_field_match(type_path, data_struct);
let set_field_idx = gen_struct_set_field_match_idx(type_path, data_struct);
let get_field_name_match = gen_struct_field_name_match(type_path, data_struct);
let get_field_name_match_idx = gen_struct_field_name_match_idx(type_path, data_struct);
let field_name_at = gen_struct_field_name_idx(type_path, data_struct);
let generics = add_trait_bounds(input.generics.clone(), vec![parse_quote!(Reflect), parse_quote!(Clone)]);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause {
fn name(&self) -> String {
#name.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(&format!("`self` was not {}", #name));
*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::Struct(self)
}
fn reflect_mut(&mut self) -> lyra_engine::reflect::ReflectMut {
lyra_engine::reflect::ReflectMut::Struct(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 lyra_engine::reflect::Reflect> {
let name = name.to_lowercase();
let name = name.as_str();
#get_field_match
}
fn field_mut(&mut self, name: &str) -> Option<&mut dyn lyra_engine::reflect::Reflect> {
let name = name.to_lowercase();
let name = name.as_str();
#get_field_mut_match
}
fn fields_len(&self) -> usize {
#field_len
}
fn field_at(&self, idx: usize) -> Option<&dyn lyra_engine::reflect::Reflect> {
#get_field_match_idx
}
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn lyra_engine::reflect::Reflect> {
#get_field_mut_match_idx
}
fn field_name_at(&self, idx: usize) -> Option<&str> {
#field_name_at
}
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> {
#get_field_name_match
}
fn field_type_at(&self, idx: usize) -> Option<&'static str> {
#get_field_name_match_idx
}
fn method(&self, name: &str) -> Option<&lyra_engine::reflect::Method> {
unimplemented!()
}
fn method_at(&self, idx: usize) -> Option<&lyra_engine::reflect::Method> {
unimplemented!()
}
fn methods_len(&self) -> usize {
unimplemented!()
}
}
}
}

View File

@ -0,0 +1,98 @@
use std::{any::TypeId, sync::Arc, ptr::NonNull};
use super::{Reflect, Tuple, ReflectRef};
#[derive(Default)]
pub struct DynamicTupleRef {
items: Vec<Box<dyn Reflect>>,
}
impl DynamicTupleRef {
/// Create a new, empty DynamicTuple.
pub fn new() -> Self {
Self::default()
}
/// Add a boxed item to the DynamicTuple, returning the index it was added at.
pub fn add_item_boxed(&mut self, item: Box<dyn Reflect>) -> usize {
let idx = self.items.len();
self.items.push(item);
idx
}
/// Add an item to the DynamicTuple, returning the index it was added at.
pub fn add_item<R: Reflect>(&mut self, item: R) -> usize {
self.add_item_boxed(Box::new(item))
}
}
impl Reflect for DynamicTupleRef {
fn name(&self) -> String {
"DynamicTuple".to_string()
}
fn type_id(&self) -> std::any::TypeId {
TypeId::of::<Self>()
}
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 Reflect) {
if let ReflectRef::Tuple(t) = val.reflect_ref() {
assert_eq!(t.items_len(), self.items.len(), "The Tuple lengths do not match");
for (idx, item) in self.items.iter_mut().enumerate() {
item.apply(t.item_at(idx).unwrap());
}
} else {
panic!("The value provided was not a Tuple: {}", val.name());
}
}
fn clone_inner(&self) -> Box<dyn Reflect> {
let mut new = DynamicTupleRef::default();
new.items = self.items.iter()
.map(|i| i.clone_inner() )
.collect();
Box::new(new)
}
fn reflect_ref(&self) -> super::ReflectRef {
super::ReflectRef::Tuple(self)
}
fn reflect_mut(&mut self) -> super::ReflectMut {
super::ReflectMut::Tuple(self)
}
fn reflect_val(&self) -> &dyn Reflect {
self
}
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
self
}
}
impl Tuple for DynamicTupleRef {
fn item_at(&self, idx: usize) -> Option<&dyn Reflect> {
self.items.get(idx)
.map(|i| i.as_ref())
}
fn item_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> {
self.items.get_mut(idx)
.map(|i| i.as_mut())
}
fn items_len(&self) -> usize {
self.items.len()
}
}

21
lyra-reflect/src/field.rs Normal file
View File

@ -0,0 +1,21 @@
use std::{any::TypeId, sync::Arc};
use super::Value;
#[derive(Clone)]
pub struct Field {
pub name: String,
pub type_id: TypeId,
pub(crate) getter: Option<Arc<dyn Fn(&Value) -> Value>>,
pub(crate) setter: Option<Arc<dyn Fn(&mut Value, Value)>>,
}
impl Field {
pub fn getter_fn(&self) -> Option<Arc<dyn Fn(&Value) -> Value>> {
self.getter.clone()
}
pub fn setter_fn(&self) -> Option<Arc<dyn Fn(&mut Value, Value)>> {
self.setter.clone()
}
}

View File

@ -0,0 +1,128 @@
use std::any::TypeId;
use lyra_reflect_derive::impl_reflect_trait_value;
use crate::List;
use crate::lyra_engine;
use super::{Reflect, ReflectRef, ReflectMut, util, TypeData};
impl_reflect_trait_value!(bool);
impl_reflect_trait_value!(char);
impl_reflect_trait_value!(u8);
impl_reflect_trait_value!(u16);
impl_reflect_trait_value!(u32);
impl_reflect_trait_value!(u64);
impl_reflect_trait_value!(u128);
impl_reflect_trait_value!(usize);
impl_reflect_trait_value!(i8);
impl_reflect_trait_value!(i16);
impl_reflect_trait_value!(i32);
impl_reflect_trait_value!(i64);
impl_reflect_trait_value!(i128);
impl_reflect_trait_value!(isize);
impl_reflect_trait_value!(f32);
impl_reflect_trait_value!(f64);
impl_reflect_trait_value!(String);
impl_reflect_trait_value!(::core::option::Option<T: Clone + Reflect>);
impl_reflect_trait_value!(::core::result::Result<T: Clone + Reflect, E: Clone + Reflect>);
impl_reflect_trait_value!(::core::ptr::NonNull<T: Reflect>);
impl<T: Clone + Reflect, const N: usize> Reflect for [T; N] {
fn name(&self) -> String {
std::any::type_name::<Self>().to_string()
}
fn type_id(&self) -> std::any::TypeId {
std::any::TypeId::of::<Self>()
}
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 Reflect) {
todo!()
}
fn clone_inner(&self) -> Box<dyn Reflect> {
Box::new(self.clone())
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Array(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Array(self)
}
fn reflect_val(&self) -> &dyn Reflect {
self
}
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
self
}
}
impl<T: Clone + Reflect> Reflect for Vec<T> {
fn name(&self) -> String {
std::any::type_name::<Self>().to_string()
}
fn type_id(&self) -> std::any::TypeId {
std::any::TypeId::of::<Self>()
}
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 Reflect) {
if let ReflectRef::List(list) = val.reflect_ref() {
self.apply_list(list);
} else {
panic!("Provided value was not a List!");
}
}
fn clone_inner(&self) -> Box<dyn Reflect> {
if let ReflectRef::List(list) = self.reflect_ref() {
util::clone_inner_list(list)
} else {
panic!("Provided value was not a List!");
}
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::List(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::List(self)
}
fn reflect_val(&self) -> &dyn Reflect {
self
}
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
self
}
}

300
lyra-reflect/src/lib.rs Normal file
View File

@ -0,0 +1,300 @@
#![feature(trait_upcasting)]
use std::{any::TypeId, any::Any, cell::{Ref, RefMut}};
use lyra_ecs::{world::World, DynamicBundle, Component, Entity, ComponentInfo, query::dynamic::DynamicType};
extern crate self as lyra_reflect;
#[allow(unused_imports)]
pub(crate) mod lyra_engine {
pub(crate) mod reflect {
pub use super::super::*;
}
}
pub mod reflected_struct;
pub use reflected_struct::*;
pub mod reflected_enum;
pub use reflected_enum::*;
pub mod reflected_tuple;
pub use reflected_tuple::*;
pub mod reflected_array;
pub use reflected_array::*;
pub mod reflected_list;
pub use reflected_list::*;
pub mod dynamic_tuple;
pub use dynamic_tuple::*;
pub mod reflected_field;
pub use reflected_field::*;
pub mod util;
pub mod field;
pub use field::*;
pub mod method;
pub use method::*;
pub mod registry;
pub use registry::*;
pub mod impl_std;
pub trait Reflect: Any {
fn name(&self) -> String;
fn type_id(&self) -> TypeId;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Apply a value to this object.
///
/// # Panics
///
/// The method panics if
/// * the type of `val` is not the same type as `self`
fn apply(&mut self, val: &dyn Reflect);
/// Clone self into a [`Box<dyn Reflect>`]
fn clone_inner(&self) -> Box<dyn Reflect>;
/// Returns a reflected reference of the value of `self`
fn reflect_ref(&self) -> ReflectRef;
/// Returns a reflected mutable reference of the value of `self`
fn reflect_mut(&mut self) -> ReflectMut;
fn reflect_val(&self) -> &dyn Reflect;
fn reflect_val_mut(&mut self) -> &mut dyn Reflect;
/// Returns a boolean indicating if this reflected object is `other_typeid`
///
/// I can't use a generic here because this trait needs to be object safe
fn is(&self, other_typeid: TypeId) -> bool {
TypeId::of::<Self>() == other_typeid
}
}
pub enum ReflectRef<'a> {
Struct(&'a dyn Struct),
Enum(&'a dyn Enum),
Tuple(&'a dyn Tuple),
Array(&'a dyn Array),
List(&'a dyn List),
Value(&'a dyn Reflect),
}
impl<'a> ReflectRef<'a> {
/// Gets the reflected reference as a struct.
///
/// # Safety
/// * If `self` is not a `Struct` variant, this will panic
pub fn as_struct(&self) -> &dyn Struct {
match self {
ReflectRef::Struct(s) => *s,
_ => panic!("`self` is not a struct variant!"),
}
}
}
pub enum ReflectMut<'a> {
Struct(&'a mut dyn Struct),
Enum(&'a mut dyn Enum),
Tuple(&'a mut dyn Tuple),
Array(&'a mut dyn Array),
List(&'a mut dyn List),
Value(&'a mut dyn Reflect),
}
impl<'a> ReflectMut<'a> {
/// Retrieves self as a [`ReflectRef`].
pub fn as_ref(&self) -> ReflectRef {
match self {
ReflectMut::Struct(v) => ReflectRef::Struct(*v),
ReflectMut::Enum(v) => ReflectRef::Enum(*v),
ReflectMut::Tuple(v) => ReflectRef::Tuple(*v),
ReflectMut::Array(v) => ReflectRef::Array(*v),
ReflectMut::List(v) => ReflectRef::List(*v),
ReflectMut::Value(v) => ReflectRef::Value(*v),
}
}
/// Gets the reflected reference as a struct.
///
/// # Safety
/// * If `self` is not a `Struct` variant, this will panic
pub fn as_struct(&self) -> &dyn Struct {
match self {
ReflectMut::Struct(s) => *s,
_ => panic!("`self` is not a struct variant!"),
}
}
/// Gets the reflected reference mutable as a struct.
///
/// # Safety
/// * If `self` is not a `Struct` variant, this will panic
pub fn as_struct_mut(&mut self) -> &mut dyn Struct {
match self {
ReflectMut::Struct(s) => *s,
_ => panic!("`self` is not a struct variant!"),
}
}
}
pub enum ReflectEither<'a> {
Ref(ReflectRef<'a>),
Mut(ReflectMut<'a>),
}
impl<'a> ReflectEither<'a> {
/// Converts `self` into a [`ReflectRef`]. Both variants can be retrieved as a reference.
/* pub fn into_ref(self) -> ReflectRef<'a> {
match self {
Self::Ref(v) => v,
Self::Mut(v) => v.as_ref()
}
} */
/// Attempts to get `self` as a [`ReflectMut`].
///
/// If `self` is a `Ref`, `None` will be returned
pub fn try_into_mut(self) -> Option<ReflectMut<'a>> {
match self {
Self::Mut(v) => Some(v),
Self::Ref(_) => None
}
}
/* pub fn as_ref(&self) -> &ReflectRef<'a> {
match self {
Self::Ref(v) => v,
Self::Mut(v) => &v.as_ref()
}
} */
pub fn is_mut(&self) -> bool {
match self {
ReflectEither::Ref(_) => false,
ReflectEither::Mut(_) => true,
}
}
}
pub struct Value {
inner: Box<dyn Reflect>,
type_id: TypeId,
ref_type_id: TypeId,
}
impl Value {
pub fn new<T: Reflect>(val: T) -> Self {
Self {
inner: Box::new(val),
type_id: TypeId::of::<T>(),
ref_type_id: TypeId::of::<&T>(),
}
}
/// Remove the stored object from Value
pub fn take<T: Reflect>(self) -> Option<T> {
(self.inner as Box<dyn Any>).downcast::<T>().ok().map(|v| *v)
}
pub fn get<T: Reflect>(&self) -> Option<&T> {
self.inner.as_any().downcast_ref::<T>()
}
pub fn get_mut<T: Reflect>(&mut self) -> Option<&mut T> {
self.inner.as_any_mut().downcast_mut::<T>()
}
}
pub trait IntoValue {
fn into_value(self) -> Value;
fn type_id() -> TypeId;
}
impl<T: Reflect> IntoValue for T {
fn into_value(self) -> Value {
Value::new(self)
}
fn type_id() -> TypeId {
TypeId::of::<T>()
}
}
pub trait FromType<T> {
fn from_type() -> Self;
}
#[derive(Clone)]
pub struct ReflectedComponent {
pub type_id: TypeId,
pub info: ComponentInfo,
//value: Value,
//from_world:
//from_world: for<'a> fn (world: &'a mut World) -> Box<dyn Reflect>,
/// Inserts component into entity in the world
fn_insert: for<'a> fn (world: &'a mut World, entity: Entity, component: &dyn Reflect),
/// Inserts component into a bundle
fn_bundle_insert: for<'a> fn (dynamic_bundle: &'a mut DynamicBundle, component: &dyn Reflect),
fn_reflect: for<'a> fn (world: &'a World, entity: Entity) -> Option<Ref<'a, dyn Reflect>>,
fn_reflect_mut: for<'a> fn (world: &'a mut World, entity: Entity) -> Option<RefMut<'a, dyn Reflect>>,
}
impl ReflectedComponent {
/// Insert the reflected component into an entity.
pub fn insert(&self, world: &mut World, entity: Entity, component: &dyn Reflect) {
(self.fn_insert)(world, entity, component);
}
/// Insert this component into a DynamicBundle
pub fn bundle_insert(&self, dynamic_bundle: &mut DynamicBundle, component: &dyn Reflect) {
(self.fn_bundle_insert)(dynamic_bundle, component)
}
/// Retrieves a reflected component from an entity.
pub fn reflect<'a>(&'a self, world: &'a World, entity: Entity) -> Option<Ref<'a, dyn Reflect>> {
(self.fn_reflect)(world, entity)
}
/// Retrieves a reflected component from an entity.
pub fn reflect_mut<'a>(&'a mut self, world: &'a mut World, entity: Entity) -> Option<RefMut<'a, dyn Reflect>> {
(self.fn_reflect_mut)(world, entity)
}
}
impl<C: Component + Reflect + Default> FromType<C> for ReflectedComponent {
fn from_type() -> Self {
ReflectedComponent {
type_id: TypeId::of::<C>(),
info: ComponentInfo::new::<C>(),
fn_insert: |world: &mut World, entity: Entity, component: &dyn Reflect| {
let mut c = C::default();
c.apply(component);
world.insert(entity, (c,));
},
fn_bundle_insert: |bundle: &mut DynamicBundle, component: &dyn Reflect| {
let mut c = C::default();
c.apply(component);
bundle.push(c);
},
fn_reflect: |world: &World, entity: Entity| {
world.view_one::<&C>(entity)
.get().map(|c| c as Ref<dyn Reflect>)
},
fn_reflect_mut: |world: &mut World, entity: Entity| {
world.view_one::<&mut C>(entity)
.get().map(|c| c as RefMut<dyn Reflect>)
},
}
}
}

212
lyra-reflect/src/method.rs Normal file
View File

@ -0,0 +1,212 @@
use std::{any::TypeId, sync::Arc};
use super::{Value, IntoValue};
#[derive(Clone)]
pub struct Method {
pub name: Option<String>,
pub arguments: Vec<TypeId>,
pub return_val: Option<TypeId>,
inner: Arc<dyn Fn(Vec<Value>) -> Option<Value>>,
}
/* impl Method {
pub fn new<'a, F, R, Args>(f: F) -> Self
where
F: MethodFunction<R, Args> + 'static,
{
let inner = move |vals: Vec<Value>| {
f.exec(vals)
};
let inner = Arc::new(inner);
Self {
name: None,
arguments: vec![],
return_val: None,
inner: inner,
}
}
/// Execute the method
pub fn exec<R, Args>(&self, args: Args) -> Option<R>
where
Args: FunctionArg,
R: 'static
{
let args = args.into_args();
if let Some(first) = args.iter().next() {
if first.type_id == TypeId::of::<Value>() {
panic!("Do not provide values to `Method::exec`, use `Method::exec_val` instead");
}
}
(self.inner)(args)
.and_then(|r| r.take()) // into R
}
/// Execute the methods using values, and returning a value
pub fn exec_val(&self, args: Vec<Value>) -> Option<Value> {
(self.inner)(args)
}
}
pub trait FunctionArg {
fn into_args(self) -> Vec<Value>;
}
/// causes confusion when providing values to `Method::exec`; it will wrap the
/// Value in another Value, causing the exec to fail.
impl<A: 'static> FunctionArg for (A,) {
fn into_args(self) -> Vec<Value> {
vec![Value::new(self.0)]
}
}
macro_rules! impl_function_arg_tuple {
( $($name: ident),+ ) => (
#[allow(non_snake_case)]
impl<$($name: 'static,)+> FunctionArg for ($($name,)+) {
fn into_args(self) -> Vec<Value> {
let ($($name,)+) = self;
vec![$(Value::new($name),)+]
}
}
);
}
impl_function_arg_tuple!{ A, B }
impl_function_arg_tuple!{ A, B, C }
impl_function_arg_tuple!{ A, B, C, D }
impl_function_arg_tuple!{ A, B, C, D, E }
impl_function_arg_tuple!{ A, B, C, D, E, F2 }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H, I }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H, I, J }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O }
impl_function_arg_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P }
impl FunctionArg for Vec<Value> {
fn into_args(self) -> Vec<Value> {
self
}
}
pub trait MethodFunction<R, Args = ()> {
fn arguments(&self) -> Vec<TypeId>;
fn return_vals(&self) -> Option<TypeId>;
fn exec(&self, vals: Vec<Value>) -> Option<Value>;
fn invoke(&self, vals: Args) -> R;
}
fn check_types<T: 'static>(val: &Value) {
let a_tyid = TypeId::of::<T>();
if val.ref_type_id == a_tyid {
// TODO: Maybe there's some dark art thing that can be done here to get a reference
panic!("You provided an owned value to the method, but a reference was expected!");
} else if val.type_id != a_tyid {
panic!("You provided incorrect types to the method (got {:?}, expected {:?})", val.type_id, a_tyid);
}
}
impl<F, R, A> MethodFunction<R, (A,)> for F
where
F: Fn(A) -> R,
R: IntoValue + 'static,
A: 'static,
{
fn arguments(&self) -> Vec<TypeId> {
vec![TypeId::of::<A>()]
}
fn return_vals(&self) -> Option<TypeId> {
Some(TypeId::of::<R>())
}
fn exec(&self, mut vals: Vec<Value>) -> Option<Value> {
let v0 = vals.remove(0);
check_types::<A>(&v0);
match v0.inner.downcast::<A>() {
Ok(v0) => {
let v0 = *v0;
return Some(Value::new((self)(v0)));
},
Err(_) => { }
}
panic!("Failure to downcast");
}
fn invoke(&self, vals: (A,)) -> R {
let (a,) = vals;
(self)(a)
}
}
macro_rules! impl_method_tuple {
( $($name: ident),+ ) => (
#[allow(non_snake_case)]
impl<F, R, $($name,)+> MethodFunction<R, ($($name,)+)> for F
where
F: Fn($($name,)+) -> R,
R: IntoValue + 'static,
$($name: 'static,)+
{
fn arguments(&self) -> Vec<TypeId> {
vec![$(TypeId::of::<$name>(),)+]
}
fn return_vals(&self) -> Option<TypeId> {
Some(TypeId::of::<R>())
}
fn exec(&self, mut vals: Vec<Value>) -> Option<Value> {
$(
let $name = vals.remove(0);
check_types::<$name>(&$name);
)+
// ensure that all values were used. If any were left, you provided the
// incorrect number of arguments
assert!(vals.is_empty());
$(
let $name = *$name.inner.downcast::<$name>()
.expect("Incorrect argument type!");
)+
let res = (self)($($name,)+);
Some(Value::new(res))
}
fn invoke(&self, vals: ($($name,)+)) -> R {
let ($($name,)+) = vals;
(self)($($name,)+)
}
}
);
}
//impl_method_tuple!{ A }
// hopefully 16 arguments in a reflected function is enough
impl_method_tuple!{ A, B }
impl_method_tuple!{ A, B, C }
impl_method_tuple!{ A, B, C, D }
impl_method_tuple!{ A, B, C, D, E }
impl_method_tuple!{ A, B, C, D, E, F2 }
impl_method_tuple!{ A, B, C, D, E, F2, G }
impl_method_tuple!{ A, B, C, D, E, F2, G, H }
impl_method_tuple!{ A, B, C, D, E, F2, G, H, I }
impl_method_tuple!{ A, B, C, D, E, F2, G, H, I, J }
impl_method_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L }
impl_method_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M }
impl_method_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N }
impl_method_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O }
impl_method_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P } */

View File

@ -0,0 +1,64 @@
use super::Reflect;
pub trait Array: Reflect {
/// Get a borrow to the element at `idx` in the array.
fn get(&self, idx: usize) -> Option<&dyn Reflect>;
/// Get a mutable borrow to the element at `idx` in the array.
fn get_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect>;
/// Returns the length of the array
fn len(&self) -> usize;
}
impl<T: Clone + Reflect, const N: usize> Array for [T; N] {
fn get(&self, idx: usize) -> Option<&dyn Reflect> {
if idx < self.len() {
Some(&self[idx])
} else {
None
}
}
fn get_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> {
if idx < self.len() {
Some(&mut self[idx])
} else {
None
}
}
fn len(&self) -> usize {
N
}
}
#[cfg(test)]
mod tests {
use crate::{Reflect, ReflectRef};
#[test]
fn simple() {
let mut arr = [0; 4];
arr[0] = 340;
arr[1] = 275;
arr[2] = 5;
arr[3] = 93;
let arr = Box::new(arr) as Box<dyn Reflect>;
if let ReflectRef::Array(arr) = arr.reflect_ref() {
let zero = arr.get(0).unwrap().as_any().downcast_ref::<i32>().unwrap();
assert_eq!(*zero, 340);
let one = arr.get(1).unwrap().as_any().downcast_ref::<i32>().unwrap();
assert_eq!(*one, 275);
let two = arr.get(2).unwrap().as_any().downcast_ref::<i32>().unwrap();
assert_eq!(*two, 5);
let three = arr.get(3).unwrap().as_any().downcast_ref::<i32>().unwrap();
assert_eq!(*three, 93);
}
}
}

View File

@ -0,0 +1,138 @@
use super::Reflect;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum EnumType {
Unit,
Tuple,
Struct,
}
pub trait Enum: Reflect {
/// Returns the enum item matching the **lowercase** name.
///
/// Will return None if the item is not in this struct, or if the num is not an instance of
/// the item matching the name. See [`Enum::has_item`] for seeing if the item exists.
fn field(&self, name: &str) -> Option<&dyn Reflect>;
/// Returns the enum item matching the **lowercase** name.
///
/// Will return None if the item is not in this struct, or if the num is not an instance of
/// the item matching the name. See [`Enum::has_item`] for seeing if the item exists.
fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect>;
fn field_at(&self, idx: usize) -> Option<&dyn Reflect>;
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect>;
/// Returns the name of the field at the index. Will only return something if this [`EnumType`]
/// is a `Struct`.
fn field_name_at(&self, idx: usize) -> Option<String>;
fn has_field(&self, name: &str) -> bool;
/// Returns the number of fields inside the variant of the enum.
fn fields_len(&self) -> usize;
/// Returns the number of variants the enum has.
fn variants_len(&self) -> usize;
/// Returns the name of the current enum item that self is.
fn variant_name(&self) -> String;
/// Returns the index of the current enum item that self is.
fn variant_index(&self) -> usize;
/// Returns a boolean indicating if self is an instance of the provided variant.
fn is_variant_name(&self, name: &str) -> bool;
/// Returns the type of the variant that self is an instance of
fn variant_type(&self) -> EnumType;
}
#[cfg(test)]
mod tests {
use lyra_reflect_derive::Reflect;
use super::EnumType;
use crate::{lyra_engine, Reflect, ReflectRef};
#[derive(Clone, Reflect)]
enum TestEnum {
Start,
Middle(u32, u64),
Error { msg: String, code: u32},
}
#[test]
fn unit_variant() {
let test_enum = TestEnum::Start;
let test_enum = Box::new(test_enum) as Box<dyn Reflect>;
if let ReflectRef::Enum(e) = test_enum.reflect_ref() {
assert_eq!(e.variant_type(), EnumType::Unit);
assert_eq!(e.variant_index(), 0);
assert_eq!(e.variants_len(), 3);
assert_eq!(e.fields_len(), 0);
assert_eq!(e.variant_name().to_lowercase(), "start".to_string());
assert!(e.is_variant_name("start"));
} else {
panic!("The reflected reference was not an Enum!");
}
}
#[test]
fn struct_variant() {
let test_enum = TestEnum::Error { msg: "sick test".to_string(), code: 420 };
let test_enum = Box::new(test_enum) as Box<dyn Reflect>;
if let ReflectRef::Enum(e) = test_enum.reflect_ref() {
assert_eq!(e.variant_type(), EnumType::Struct);
assert_eq!(e.variant_index(), 2);
assert_eq!(e.variants_len(), 3);
assert_eq!(e.fields_len(), 2);
assert_eq!(e.variant_name().to_lowercase(), "error".to_string());
assert!(e.is_variant_name("error"));
let msg = e.field("msg").unwrap();
let msg = msg.as_any().downcast_ref::<String>().unwrap();
assert_eq!(msg, "sick test");
let code: &dyn Reflect = e.field("code").unwrap();
let code = code.as_any().downcast_ref::<u32>().unwrap();
assert_eq!(*code, 420);
} else {
panic!("The reflected reference was not an Enum!");
}
}
#[test]
fn tuple_variant() {
let test_enum = TestEnum::Middle(420, 69);
let test_enum = Box::new(test_enum) as Box<dyn Reflect>;
if let ReflectRef::Enum(e) = test_enum.reflect_ref() {
assert_eq!(e.variant_type(), EnumType::Tuple);
assert_eq!(e.variant_index(), 1);
assert_eq!(e.variants_len(), 3);
assert_eq!(e.fields_len(), 2);
assert_eq!(e.variant_name().to_lowercase(), "middle".to_string());
assert!(e.is_variant_name("middle"));
let msg = e.field("msg");
assert!(msg.is_none());
let code = e.field("code");
assert!(code.is_none());
let a = e.field_at(0).unwrap();
let a = a.as_any().downcast_ref::<u32>().unwrap();
assert_eq!(*a, 420);
let b = e.field_at(1).unwrap();
let b = b.as_any().downcast_ref::<u64>().unwrap();
assert_eq!(*b, 69);
} else {
panic!("The reflected reference was not an Enum!");
}
}
}

View File

@ -0,0 +1,31 @@
use super::{ReflectEither, Reflect, Struct};
/// A struct that can set fields on different types of reflected values.
pub struct ReflectField<'a> {
val: &'a dyn Reflect,
getter: fn() -> &'a dyn Reflect,
setter: fn(new_val: &'a dyn Reflect),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FieldType {
Struct,
Enum,
Tuple,
}
enum FieldIdent {
Index(usize),
Named(String)
}
impl<'a> ReflectField<'a> {
pub fn new(reflect: &'a dyn Reflect, getter: fn() -> &'a dyn Reflect, setter: fn(new_val: &'a dyn Reflect)) -> Self {
Self {
val: reflect,
getter,
setter
}
}
}

View File

@ -0,0 +1,147 @@
use std::any::TypeId;
use super::{Reflect, util};
pub trait List: Reflect {
fn get(&self, idx: usize) -> Option<&dyn Reflect>;
fn get_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect>;
/// Push a value to the end of the List. If `item` is not the same type of the list,
/// it will be returned as [`Result::Err`].
fn push_back(&mut self, item: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>>;
fn push_front(&mut self, item: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>>;
/// Removes an item at `idx` from the List and returns the owned value.
fn remove_at(&mut self, idx: usize) -> Option<Box<dyn Reflect>>;
/// Removes the last element from the vector and returns it, `None` if it is empty.
fn pop_back(&mut self) -> Option<Box<dyn Reflect>>;
/// Removes the first element from the vector and returns it, `None` if it is empty.
///
/// Keep in mind this is slow if the List is not a `VecDeque`, or some other list type that is
/// optimized for removing from the front.
fn pop_front(&mut self) -> Option<Box<dyn Reflect>>;
/// Append a list to this list, removing the elements from `other`.
fn append(&mut self, other: &mut dyn List);
/// Reserves at least `additional` more space in the List. The spaces are not
/// initialized to anything.
fn reserve(&mut self, additional: usize);
/// Returns the length of the list
fn len(&self) -> usize;
/// Returns the total number of elements the list can hold without reallocating.
fn capacity(&self) -> usize;
/// Returns a boolean indicating if the list is empty.
fn is_empty(&self) -> bool;
/// Returns the TypeId of the elements in the list
fn elements_id(&self) -> TypeId;
fn apply_list(&mut self, other: &dyn List);
/// Creates a new, and empty list of the same type.
fn create_empty(&self) -> Box<dyn List>;
}
impl<T: Reflect + Clone> List for Vec<T> {
fn get(&self, idx: usize) -> Option<&dyn Reflect> {
if idx < self.len() {
Some(&self[idx])
} else {
None
}
}
fn get_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> {
if idx < self.len() {
Some(&mut self[idx])
} else {
None
}
}
fn push_back(&mut self, item: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
if item.is(TypeId::of::<T>()) {
let item = *(item as Box<dyn std::any::Any>).downcast::<T>()
.unwrap();
self.push(item);
Ok(())
} else {
Err(item)
}
}
fn push_front(&mut self, item: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
unimplemented!("push_front for `List` trait implementation of a `Vec<T>`. Doing so is slow!")
}
fn remove_at(&mut self, idx: usize) -> Option<Box<dyn Reflect>> {
if idx < self.len() {
let removed = Box::new(self.remove(idx));
Some(removed)
} else {
None
}
}
fn pop_back(&mut self) -> Option<Box<dyn Reflect>> {
let popped = Box::new(self.pop());
Some(popped)
}
fn pop_front(&mut self) -> Option<Box<dyn Reflect>> {
unimplemented!("pop_front for `List` trait implementation of a `Vec<T>`. Doing so is slow!")
}
fn append(&mut self, other: &mut dyn List) {
assert!(self.elements_id() == other.elements_id(),
"Provided a List that contains elements that don't match what's in Self!");
self.reserve(other.len());
// pop_back and reverse later is probably the best to use here. I don't have iterators
// written for `List` yet, and an implementation of pop_front for a `Vec` would be slow.
let mut other_elements = vec![];
while let Some(el) = other.pop_back() {
let el = *(el as Box<dyn std::any::Any>).downcast::<T>()
.expect("msg");
other_elements.push(el);
}
other_elements.reverse();
self.append(&mut other_elements);
}
fn len(&self) -> usize {
self.len()
}
fn is_empty(&self) -> bool {
self.is_empty()
}
fn elements_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn apply_list(&mut self, other: &dyn List) {
util::apply_list(self, other);
}
fn create_empty(&self) -> Box<dyn List> {
Box::new(Vec::<T>::new())
}
fn reserve(&mut self, additional: usize) {
self.reserve(additional);
}
fn capacity(&self) -> usize {
self.capacity()
}
}

View File

@ -0,0 +1,108 @@
use std::{any::{TypeId, type_name, Any}, collections::HashMap, marker::PhantomData, sync::Arc};
use super::{field::Field, method::Method, Reflect, Value};
/// A trait that a struct would implement
pub trait Struct: Reflect {
/// Attempt to get a borrow of a field.
fn field(&self, name: &str) -> Option<&dyn Reflect>;
/// Attempt to get a mutable borrow of a field.
fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect>;
/// Returns the number of fields in the struct.
fn fields_len(&self) -> usize;
/// Returns a borrow of a field at `idx`. Returns None if the index is out of range.
fn field_at(&self, idx: usize) -> Option<&dyn Reflect>;
/// Returns a mutable borrow of a field at `idx`. Returns None if the index is out of range.
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect>;
/// Returns the name of the field at the index.
fn field_name_at(&self, idx: usize) -> Option<&str>;
/// Sets the field named `name`. Returns a boolean indicating if it was successful.
fn set_field(&mut self, name: &str, val: &dyn Reflect) -> bool;
/// Sets the field at `idx`. Returns a boolean indicating if it was successful.
fn set_field_at(&mut self, idx: usize, val: &dyn Reflect) -> bool;
/// Returns the type of the field named `name`.
fn field_type(&self, name: &str) -> Option<&'static str>;
/// Returns the type of the field at `idx`.
fn field_type_at(&self, idx: usize) -> Option<&'static str>;
// not really sure if any of these can actually be used with the new reflection system
fn method(&self, name: &str) -> Option<&Method>;
fn method_at(&self, idx: usize) -> Option<&Method>;
fn methods_len(&self) -> usize;
}
#[cfg(test)]
mod tests {
use std::any::TypeId;
use lyra_reflect_derive::Reflect;
use crate::{Reflect, ReflectRef, ReflectMut};
use super::Struct;
use crate::lyra_engine;
#[derive(Clone, Copy, Reflect)]
struct Vec2 {
x: f32,
y: f32,
}
#[test]
fn struct_ref() {
let v = Vec2 {
x: 5.0,
y: 10.0
};
let v = Box::new(v) as Box<dyn Reflect>;
if let ReflectRef::Struct(s) = v.reflect_ref() {
let x = s.field("x").unwrap();
let x = x.as_any().downcast_ref::<f32>().unwrap();
assert_eq!(*x, 5.0);
let y = s.field("y").unwrap();
let y = y.as_any().downcast_ref::<f32>().unwrap();
assert_eq!(*y, 10.0);
}
}
#[test]
fn struct_mut() {
let v = Vec2 {
x: 5.0,
y: 10.0
};
let mut v = Box::new(v) as Box<dyn Reflect>;
if let ReflectMut::Struct(s) = v.reflect_mut() {
let x = s.field_mut("x").unwrap();
let x = x.as_any_mut().downcast_mut::<f32>().unwrap();
*x = *x * 2.0;
let y = s.field_mut("y").unwrap();
let y = y.as_any_mut().downcast_mut::<f32>().unwrap();
*y = *y * 2.0;
}
if let ReflectRef::Struct(s) = v.reflect_ref() {
let x = s.field("x").unwrap();
let x = x.as_any().downcast_ref::<f32>().unwrap();
assert_eq!(*x, 10.0);
let y = s.field("y").unwrap();
let y = y.as_any().downcast_ref::<f32>().unwrap();
assert_eq!(*y, 20.0);
}
}
}

View File

@ -0,0 +1,107 @@
use super::{Reflect, ReflectRef, ReflectMut};
pub trait Tuple: Reflect {
fn item_at(&self, idx: usize) -> Option<&dyn Reflect>;
fn item_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect>;
fn items_len(&self) -> usize;
}
macro_rules! impl_reflect_tuple {
( $count: tt, $( ($name: ident, $idx: tt) ),+ ) => (
impl<$($name: Reflect + Clone),+> Reflect for ($($name,)+) {
fn name(&self) -> String {
std::any::type_name::<Self>().to_string()
}
fn type_id(&self) -> std::any::TypeId {
std::any::TypeId::of::<Self>()
}
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 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 Reflect> {
Box::new(self.clone())
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Tuple(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Tuple(self)
}
fn reflect_val(&self) -> &dyn Reflect {
self
}
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
self
}
}
impl<$($name: Reflect + Clone,)+> Tuple for ($($name,)+) {
fn item_at(&self, at_idx: usize) -> Option<&dyn Reflect> {
match at_idx {
$( $idx => Some(&self.$idx), )+
_ => None,
}
}
fn item_at_mut(&mut self, at_idx: usize) -> Option<&mut dyn Reflect> {
match at_idx {
$( $idx => Some(&mut self.$idx), )+
_ => None,
}
}
fn items_len(&self) -> usize {
$count
}
}
);
}
// this is absolute cancer, but I can't be asked to make something nicer looking
impl_reflect_tuple!(1, (A, 0) );
impl_reflect_tuple!(2, (A, 0), (B, 1) );
impl_reflect_tuple!(3, (A, 0), (B, 1), (C, 2) );
impl_reflect_tuple!(4, (A, 0), (B, 1), (C, 2), (D, 3) );
impl_reflect_tuple!(5, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4) );
impl_reflect_tuple!(6, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5) );
impl_reflect_tuple!(7, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6) );
impl_reflect_tuple!(8, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7) );
impl_reflect_tuple!(9, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8) );
impl_reflect_tuple!(10, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8),
(J, 9) );
impl_reflect_tuple!(11, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8),
(J, 9), (K, 10) );
impl_reflect_tuple!(12, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8),
(J, 9), (K, 10), (L, 11) );
impl_reflect_tuple!(13, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8),
(J, 9), (K, 10), (L, 11), (M, 12) );
impl_reflect_tuple!(14, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8),
(J, 9), (K, 10), (L, 11), (M, 12), (N, 13) );
impl_reflect_tuple!(15, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8),
(J, 9), (K, 10), (L, 11), (M, 12), (N, 13), (O, 14) );
impl_reflect_tuple!(16, (A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6), (H, 7), (I, 8),
(J, 9), (K, 10), (L, 11), (M, 12), (N, 13), (O, 14), (P, 15) );

View File

@ -0,0 +1,99 @@
use std::{any::{TypeId, type_name}, collections::HashMap, ops::Deref};
/// Storage for
#[derive(Default, Clone)]
pub struct TypeRegistry {
inner: HashMap<TypeId, RegisteredType>,
}
impl TypeRegistry {
pub fn new() -> Self {
Self::default()
}
/// Get registered type from the registry.
pub fn get_type(&self, type_id: TypeId) -> Option<&RegisteredType> {
self.inner.get(&type_id)
}
/// Get a mutable borrow of a registered type from the registry.
pub fn get_type_mut(&mut self, type_id: TypeId) -> Option<&mut RegisteredType> {
self.inner.get_mut(&type_id)
}
pub fn register_type<T>(&mut self)
where
T: AsRegisteredType + 'static
{
self.inner.insert(TypeId::of::<T>(), T::as_registered_type());
}
/// Check if the registry has a type.
pub fn has_type(&self, type_id: TypeId) -> bool {
self.inner.contains_key(&type_id)
}
pub fn add_registered_type(&mut self, type_id: TypeId, registered: RegisteredType) {
self.inner.insert(type_id, registered);
}
}
pub trait TypeData: std::any::Any {
fn as_any(&self) -> &dyn std::any::Any;
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
fn boxed_clone(&self) -> Box<dyn TypeData>;
}
#[derive(Default)]
pub struct RegisteredType {
data: HashMap<TypeId, Box<dyn TypeData>>,
pub(crate) data_names: HashMap<TypeId, String>,
}
impl Clone for RegisteredType {
fn clone(&self) -> Self {
let cloned_data = self.data.iter()
.map(|(k, v)| (k.clone(), v.deref().boxed_clone()))
.collect();
Self {
data: cloned_data,
data_names: self.data_names.clone(),
}
}
}
impl RegisteredType {
pub fn new() -> Self {
Self::default()
}
pub fn get_data<D>(&self) -> Option<&D>
where
D: TypeData
{
self.data.get(&TypeId::of::<D>())
.and_then(|b| b.as_ref().as_any().downcast_ref())
}
pub fn get_data_mut<D>(&mut self) -> Option<&mut D>
where
D: TypeData
{
self.data.get_mut(&TypeId::of::<D>())
.and_then(|b| b.as_mut().as_any_mut().downcast_mut())
}
pub fn add_data<D>(&mut self, data: D)
where
D: TypeData
{
self.data.insert(TypeId::of::<D>(), Box::new(data));
self.data_names.insert(TypeId::of::<D>(), type_name::<D>().to_string());
}
}
pub trait AsRegisteredType {
fn as_registered_type() -> RegisteredType;
}

29
lyra-reflect/src/util.rs Normal file
View File

@ -0,0 +1,29 @@
use super::List;
/// A helper method that implements apply_list for you
pub fn apply_list(apply_to: &mut dyn List, other: &dyn List) {
assert_eq!(apply_to.len(), other.len(),
"Lists must be the same length when applying one to the other!");
let len = apply_to.len();
for i in 0..len {
let el = apply_to.get_mut(i).unwrap();
el.apply(other.get(i).unwrap());
}
}
/// A helper method that implements [`Reflect::clone_inner`] for you.
pub fn clone_inner_list(clone: &dyn List) -> Box<dyn List> {
let mut empty = clone.create_empty();
empty.reserve(clone.len());
for i in 0..clone.len() {
let el = clone.get(i).unwrap();
let el_clone = el.clone_inner();
// ignore failures, they shouldn't happen since `empty` was cloned from
// `clone`, so they're the same type.
let _ = empty.push_back(el_clone);
}
empty
}