Add lyra-reflect
ci/woodpecker/push/debug Pipeline failed
Details
ci/woodpecker/push/debug Pipeline failed
Details
This commit is contained in:
parent
d075fd5d5f
commit
52e58b1ca5
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" }
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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>)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 } */
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) );
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue