From 52e58b1ca51c263ad05fad6a8adba29fd2f860f9 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 30 Dec 2023 18:55:05 -0500 Subject: [PATCH] Add lyra-reflect --- Cargo.lock | 18 + Cargo.toml | 3 +- lyra-reflect/Cargo.toml | 10 + lyra-reflect/lyra-reflect-derive/Cargo.toml | 14 + .../lyra-reflect-derive/src/enum_derive.rs | 607 ++++++++++++++++++ lyra-reflect/lyra-reflect-derive/src/lib.rs | 121 ++++ .../lyra-reflect-derive/src/struct_derive.rs | 392 +++++++++++ lyra-reflect/src/dynamic_tuple.rs | 98 +++ lyra-reflect/src/field.rs | 21 + lyra-reflect/src/impl_std.rs | 128 ++++ lyra-reflect/src/lib.rs | 300 +++++++++ lyra-reflect/src/method.rs | 212 ++++++ lyra-reflect/src/reflected_array.rs | 64 ++ lyra-reflect/src/reflected_enum.rs | 138 ++++ lyra-reflect/src/reflected_field.rs | 31 + lyra-reflect/src/reflected_list.rs | 147 +++++ lyra-reflect/src/reflected_struct.rs | 108 ++++ lyra-reflect/src/reflected_tuple.rs | 107 +++ lyra-reflect/src/registry.rs | 99 +++ lyra-reflect/src/util.rs | 29 + 20 files changed, 2646 insertions(+), 1 deletion(-) create mode 100644 lyra-reflect/Cargo.toml create mode 100644 lyra-reflect/lyra-reflect-derive/Cargo.toml create mode 100644 lyra-reflect/lyra-reflect-derive/src/enum_derive.rs create mode 100644 lyra-reflect/lyra-reflect-derive/src/lib.rs create mode 100644 lyra-reflect/lyra-reflect-derive/src/struct_derive.rs create mode 100644 lyra-reflect/src/dynamic_tuple.rs create mode 100644 lyra-reflect/src/field.rs create mode 100644 lyra-reflect/src/impl_std.rs create mode 100644 lyra-reflect/src/lib.rs create mode 100644 lyra-reflect/src/method.rs create mode 100644 lyra-reflect/src/reflected_array.rs create mode 100644 lyra-reflect/src/reflected_enum.rs create mode 100644 lyra-reflect/src/reflected_field.rs create mode 100644 lyra-reflect/src/reflected_list.rs create mode 100644 lyra-reflect/src/reflected_struct.rs create mode 100644 lyra-reflect/src/reflected_tuple.rs create mode 100644 lyra-reflect/src/registry.rs create mode 100644 lyra-reflect/src/util.rs diff --git a/Cargo.lock b/Cargo.lock index 46f04d3..c6b5c4b 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 6945df6..6f5a56d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/lyra-reflect/Cargo.toml b/lyra-reflect/Cargo.toml new file mode 100644 index 0000000..db57f34 --- /dev/null +++ b/lyra-reflect/Cargo.toml @@ -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" } \ No newline at end of file diff --git a/lyra-reflect/lyra-reflect-derive/Cargo.toml b/lyra-reflect/lyra-reflect-derive/Cargo.toml new file mode 100644 index 0000000..f2de453 --- /dev/null +++ b/lyra-reflect/lyra-reflect-derive/Cargo.toml @@ -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" diff --git a/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs b/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs new file mode 100644 index 0000000..8604151 --- /dev/null +++ b/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs @@ -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::() + .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::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 { + #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 + } + } + }) +} + diff --git a/lyra-reflect/lyra-reflect-derive/src/lib.rs b/lyra-reflect/lyra-reflect-derive/src/lib.rs new file mode 100644 index 0000000..766b278 --- /dev/null +++ b/lyra-reflect/lyra-reflect-derive/src/lib.rs @@ -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 +} + +impl syn::parse::Parse for ReflectDef { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attributes = input.call(Attribute::parse_outer)?; + let type_path = Path::parse_mod_style(input)?; + let mut generics = input.parse::()?; + 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::() + .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 + } + } + }) +} + +pub(crate) fn add_trait_bounds(mut generics: Generics, add_bounds: Vec) -> 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 +} \ 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 new file mode 100644 index 0000000..34368f6 --- /dev/null +++ b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs @@ -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::() +/// .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::() +/// .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::() +/// .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::() +/// .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::() + .expect(&format!("`self` was not {}", #name)); + *self = val.clone(); + } + + fn clone_inner(&self) -> Box { + 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!() + } + } + } +} \ No newline at end of file diff --git a/lyra-reflect/src/dynamic_tuple.rs b/lyra-reflect/src/dynamic_tuple.rs new file mode 100644 index 0000000..8c5ad32 --- /dev/null +++ b/lyra-reflect/src/dynamic_tuple.rs @@ -0,0 +1,98 @@ +use std::{any::TypeId, sync::Arc, ptr::NonNull}; + +use super::{Reflect, Tuple, ReflectRef}; + +#[derive(Default)] +pub struct DynamicTupleRef { + items: Vec>, +} + +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) -> 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(&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::() + } + + 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 { + 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() + } +} \ No newline at end of file diff --git a/lyra-reflect/src/field.rs b/lyra-reflect/src/field.rs new file mode 100644 index 0000000..4b24289 --- /dev/null +++ b/lyra-reflect/src/field.rs @@ -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 Value>>, + pub(crate) setter: Option>, +} + +impl Field { + pub fn getter_fn(&self) -> Option Value>> { + self.getter.clone() + } + + pub fn setter_fn(&self) -> Option> { + self.setter.clone() + } +} \ No newline at end of file diff --git a/lyra-reflect/src/impl_std.rs b/lyra-reflect/src/impl_std.rs new file mode 100644 index 0000000..8128789 --- /dev/null +++ b/lyra-reflect/src/impl_std.rs @@ -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); +impl_reflect_trait_value!(::core::result::Result); + +impl_reflect_trait_value!(::core::ptr::NonNull); + +impl Reflect for [T; N] { + fn name(&self) -> String { + std::any::type_name::().to_string() + } + + fn type_id(&self) -> std::any::TypeId { + std::any::TypeId::of::() + } + + 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 { + 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 Reflect for Vec { + fn name(&self) -> String { + std::any::type_name::().to_string() + } + + fn type_id(&self) -> std::any::TypeId { + std::any::TypeId::of::() + } + + 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 { + 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 + } +} \ No newline at end of file diff --git a/lyra-reflect/src/lib.rs b/lyra-reflect/src/lib.rs new file mode 100644 index 0000000..8e123c0 --- /dev/null +++ b/lyra-reflect/src/lib.rs @@ -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`] + fn clone_inner(&self) -> Box; + + /// 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::() == 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> { + 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, + type_id: TypeId, + ref_type_id: TypeId, +} + +impl Value { + pub fn new(val: T) -> Self { + Self { + inner: Box::new(val), + type_id: TypeId::of::(), + ref_type_id: TypeId::of::<&T>(), + } + } + + /// Remove the stored object from Value + pub fn take(self) -> Option { + (self.inner as Box).downcast::().ok().map(|v| *v) + } + + pub fn get(&self) -> Option<&T> { + self.inner.as_any().downcast_ref::() + } + + pub fn get_mut(&mut self) -> Option<&mut T> { + self.inner.as_any_mut().downcast_mut::() + } +} + +pub trait IntoValue { + fn into_value(self) -> Value; + fn type_id() -> TypeId; +} + +impl IntoValue for T { + fn into_value(self) -> Value { + Value::new(self) + } + + fn type_id() -> TypeId { + TypeId::of::() + } +} + +pub trait FromType { + 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, + /// 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>, + fn_reflect_mut: for<'a> fn (world: &'a mut World, entity: Entity) -> Option>, +} + +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> { + (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> { + (self.fn_reflect_mut)(world, entity) + } +} + +impl FromType for ReflectedComponent { + fn from_type() -> Self { + ReflectedComponent { + type_id: TypeId::of::(), + info: ComponentInfo::new::(), + 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) + }, + fn_reflect_mut: |world: &mut World, entity: Entity| { + world.view_one::<&mut C>(entity) + .get().map(|c| c as RefMut) + }, + } + } +} \ No newline at end of file diff --git a/lyra-reflect/src/method.rs b/lyra-reflect/src/method.rs new file mode 100644 index 0000000..30d2652 --- /dev/null +++ b/lyra-reflect/src/method.rs @@ -0,0 +1,212 @@ +use std::{any::TypeId, sync::Arc}; + +use super::{Value, IntoValue}; + +#[derive(Clone)] +pub struct Method { + pub name: Option, + pub arguments: Vec, + pub return_val: Option, + inner: Arc) -> Option>, +} + +/* impl Method { + pub fn new<'a, F, R, Args>(f: F) -> Self + where + F: MethodFunction + 'static, + { + let inner = move |vals: Vec| { + f.exec(vals) + }; + let inner = Arc::new(inner); + + Self { + name: None, + arguments: vec![], + return_val: None, + inner: inner, + } + } + + /// Execute the method + pub fn exec(&self, args: Args) -> Option + where + Args: FunctionArg, + R: 'static + { + let args = args.into_args(); + + if let Some(first) = args.iter().next() { + if first.type_id == TypeId::of::() { + 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) -> Option { + (self.inner)(args) + } +} + +pub trait FunctionArg { + fn into_args(self) -> Vec; +} + +/// causes confusion when providing values to `Method::exec`; it will wrap the +/// Value in another Value, causing the exec to fail. +impl FunctionArg for (A,) { + fn into_args(self) -> Vec { + 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 { + 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 { + fn into_args(self) -> Vec { + self + } +} + +pub trait MethodFunction { + fn arguments(&self) -> Vec; + fn return_vals(&self) -> Option; + fn exec(&self, vals: Vec) -> Option; + fn invoke(&self, vals: Args) -> R; +} + +fn check_types(val: &Value) { + let a_tyid = TypeId::of::(); + + 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 MethodFunction for F +where + F: Fn(A) -> R, + R: IntoValue + 'static, + A: 'static, +{ + fn arguments(&self) -> Vec { + vec![TypeId::of::()] + } + + fn return_vals(&self) -> Option { + Some(TypeId::of::()) + } + + fn exec(&self, mut vals: Vec) -> Option { + let v0 = vals.remove(0); + check_types::(&v0); + + match v0.inner.downcast::() { + 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 MethodFunction for F + where + F: Fn($($name,)+) -> R, + R: IntoValue + 'static, + $($name: 'static,)+ + { + fn arguments(&self) -> Vec { + vec![$(TypeId::of::<$name>(),)+] + } + + fn return_vals(&self) -> Option { + Some(TypeId::of::()) + } + + fn exec(&self, mut vals: Vec) -> Option { + $( + 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 } */ \ No newline at end of file diff --git a/lyra-reflect/src/reflected_array.rs b/lyra-reflect/src/reflected_array.rs new file mode 100644 index 0000000..34c2075 --- /dev/null +++ b/lyra-reflect/src/reflected_array.rs @@ -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 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; + + if let ReflectRef::Array(arr) = arr.reflect_ref() { + let zero = arr.get(0).unwrap().as_any().downcast_ref::().unwrap(); + assert_eq!(*zero, 340); + + let one = arr.get(1).unwrap().as_any().downcast_ref::().unwrap(); + assert_eq!(*one, 275); + + let two = arr.get(2).unwrap().as_any().downcast_ref::().unwrap(); + assert_eq!(*two, 5); + + let three = arr.get(3).unwrap().as_any().downcast_ref::().unwrap(); + assert_eq!(*three, 93); + } + } +} \ No newline at end of file diff --git a/lyra-reflect/src/reflected_enum.rs b/lyra-reflect/src/reflected_enum.rs new file mode 100644 index 0000000..1b5ad01 --- /dev/null +++ b/lyra-reflect/src/reflected_enum.rs @@ -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; + + 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; + + 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; + + 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::().unwrap(); + assert_eq!(msg, "sick test"); + + let code: &dyn Reflect = e.field("code").unwrap(); + let code = code.as_any().downcast_ref::().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; + + 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::().unwrap(); + assert_eq!(*a, 420); + + let b = e.field_at(1).unwrap(); + let b = b.as_any().downcast_ref::().unwrap(); + assert_eq!(*b, 69); + } else { + panic!("The reflected reference was not an Enum!"); + } + } +} \ No newline at end of file diff --git a/lyra-reflect/src/reflected_field.rs b/lyra-reflect/src/reflected_field.rs new file mode 100644 index 0000000..2d2c8fe --- /dev/null +++ b/lyra-reflect/src/reflected_field.rs @@ -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 + } + } +} \ No newline at end of file diff --git a/lyra-reflect/src/reflected_list.rs b/lyra-reflect/src/reflected_list.rs new file mode 100644 index 0000000..effe95f --- /dev/null +++ b/lyra-reflect/src/reflected_list.rs @@ -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) -> Result<(), Box>; + fn push_front(&mut self, item: Box) -> Result<(), Box>; + + /// Removes an item at `idx` from the List and returns the owned value. + fn remove_at(&mut self, idx: usize) -> Option>; + + /// Removes the last element from the vector and returns it, `None` if it is empty. + fn pop_back(&mut self) -> Option>; + + /// 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>; + + /// 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; +} + +impl List for Vec { + 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) -> Result<(), Box> { + if item.is(TypeId::of::()) { + let item = *(item as Box).downcast::() + .unwrap(); + self.push(item); + + Ok(()) + } else { + Err(item) + } + } + + fn push_front(&mut self, item: Box) -> Result<(), Box> { + unimplemented!("push_front for `List` trait implementation of a `Vec`. Doing so is slow!") + } + + fn remove_at(&mut self, idx: usize) -> Option> { + if idx < self.len() { + let removed = Box::new(self.remove(idx)); + Some(removed) + } else { + None + } + } + + fn pop_back(&mut self) -> Option> { + let popped = Box::new(self.pop()); + Some(popped) + } + + fn pop_front(&mut self) -> Option> { + unimplemented!("pop_front for `List` trait implementation of a `Vec`. 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).downcast::() + .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::() + } + + fn apply_list(&mut self, other: &dyn List) { + util::apply_list(self, other); + } + + fn create_empty(&self) -> Box { + Box::new(Vec::::new()) + } + + fn reserve(&mut self, additional: usize) { + self.reserve(additional); + } + + fn capacity(&self) -> usize { + self.capacity() + } +} \ No newline at end of file diff --git a/lyra-reflect/src/reflected_struct.rs b/lyra-reflect/src/reflected_struct.rs new file mode 100644 index 0000000..83a78cf --- /dev/null +++ b/lyra-reflect/src/reflected_struct.rs @@ -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; + + if let ReflectRef::Struct(s) = v.reflect_ref() { + let x = s.field("x").unwrap(); + let x = x.as_any().downcast_ref::().unwrap(); + assert_eq!(*x, 5.0); + + let y = s.field("y").unwrap(); + let y = y.as_any().downcast_ref::().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; + + if let ReflectMut::Struct(s) = v.reflect_mut() { + let x = s.field_mut("x").unwrap(); + let x = x.as_any_mut().downcast_mut::().unwrap(); + *x = *x * 2.0; + + let y = s.field_mut("y").unwrap(); + let y = y.as_any_mut().downcast_mut::().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::().unwrap(); + assert_eq!(*x, 10.0); + + let y = s.field("y").unwrap(); + let y = y.as_any().downcast_ref::().unwrap(); + assert_eq!(*y, 20.0); + } + } +} \ No newline at end of file diff --git a/lyra-reflect/src/reflected_tuple.rs b/lyra-reflect/src/reflected_tuple.rs new file mode 100644 index 0000000..51f0849 --- /dev/null +++ b/lyra-reflect/src/reflected_tuple.rs @@ -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::().to_string() + } + + fn type_id(&self) -> std::any::TypeId { + std::any::TypeId::of::() + } + + 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::() + .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) -> 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) ); diff --git a/lyra-reflect/src/registry.rs b/lyra-reflect/src/registry.rs new file mode 100644 index 0000000..5b264c8 --- /dev/null +++ b/lyra-reflect/src/registry.rs @@ -0,0 +1,99 @@ +use std::{any::{TypeId, type_name}, collections::HashMap, ops::Deref}; + +/// Storage for +#[derive(Default, Clone)] +pub struct TypeRegistry { + inner: HashMap, +} + +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(&mut self) + where + T: AsRegisteredType + 'static + { + self.inner.insert(TypeId::of::(), 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; +} + +#[derive(Default)] +pub struct RegisteredType { + data: HashMap>, + pub(crate) data_names: HashMap, +} + +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(&self) -> Option<&D> + where + D: TypeData + { + self.data.get(&TypeId::of::()) + .and_then(|b| b.as_ref().as_any().downcast_ref()) + } + + pub fn get_data_mut(&mut self) -> Option<&mut D> + where + D: TypeData + { + self.data.get_mut(&TypeId::of::()) + .and_then(|b| b.as_mut().as_any_mut().downcast_mut()) + } + + pub fn add_data(&mut self, data: D) + where + D: TypeData + { + self.data.insert(TypeId::of::(), Box::new(data)); + self.data_names.insert(TypeId::of::(), type_name::().to_string()); + } +} + +pub trait AsRegisteredType { + fn as_registered_type() -> RegisteredType; +} \ No newline at end of file diff --git a/lyra-reflect/src/util.rs b/lyra-reflect/src/util.rs new file mode 100644 index 0000000..65d1a9d --- /dev/null +++ b/lyra-reflect/src/util.rs @@ -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 { + 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 +} \ No newline at end of file