From a78daea34d20f5576767d2c271af2abddcc67762 Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Tue, 9 Jan 2024 19:08:30 +0300 Subject: [PATCH] moved ComponentProvider trait to fyrox_code + added proc macro for it --- fyrox-core-derive/src/component.rs | 139 ++++++++++++++++++++++++ fyrox-core-derive/src/lib.rs | 10 ++ fyrox-core-derive/tests/it/component.rs | 79 ++++++++++++++ fyrox-core-derive/tests/it/main.rs | 1 + fyrox-core/src/type_traits.rs | 90 ++++++++++++++- fyrox-scripts/src/camera.rs | 7 +- src/engine/mod.rs | 12 +- src/engine/task.rs | 3 +- src/scene/node/mod.rs | 2 +- src/script/mod.rs | 26 ++--- src/utils/component.rs | 73 ------------- src/utils/mod.rs | 1 - 12 files changed, 337 insertions(+), 106 deletions(-) create mode 100644 fyrox-core-derive/src/component.rs create mode 100644 fyrox-core-derive/tests/it/component.rs delete mode 100644 src/utils/component.rs diff --git a/fyrox-core-derive/src/component.rs b/fyrox-core-derive/src/component.rs new file mode 100644 index 000000000..0e8d4ae6c --- /dev/null +++ b/fyrox-core-derive/src/component.rs @@ -0,0 +1,139 @@ +use darling::*; +use proc_macro2::{TokenStream as TokenStream2, TokenStream}; +use quote::*; +use syn::*; + +#[derive(FromDeriveInput)] +#[darling(attributes(component), supports(struct_any, enum_any))] +pub struct TypeArgs { + pub ident: Ident, + pub generics: Generics, + pub data: ast::Data, +} + +#[derive(FromField, Clone)] +#[darling(attributes(component))] +pub struct FieldArgs { + pub ident: Option, + pub ty: Type, + #[darling(default)] + pub include: bool, + #[darling(default)] + pub path: Option, + #[darling(default)] + pub dest_type: Option, +} + +#[derive(FromVariant)] +#[darling(attributes(component))] +pub struct VariantArgs { + pub ident: Ident, + pub fields: ast::Fields, +} + +pub fn impl_type_uuid_provider(ast: DeriveInput) -> TokenStream2 { + let ty_args = TypeArgs::from_derive_input(&ast).unwrap(); + match &ty_args.data { + ast::Data::Struct(ref field_args) => impl_type_uuid_provider_struct(&ty_args, field_args), + ast::Data::Enum(_) => unimplemented!(), + } +} + +fn create_field_components( + // false if enum variant + is_struct: bool, + fields: &[FieldArgs], + field_style: ast::Style, + mutable: bool, +) -> Vec { + if field_style == ast::Style::Unit { + // `Unit` struct/enum variant has no components. + return Default::default(); + } + + let prefix = if is_struct { + if mutable { + Some(quote!(&mut self.)) + } else { + Some(quote!(&self.)) + } + } else { + None + }; + + fields + .iter() + .filter(|arg| arg.include) + .map(|arg| { + let ident = &arg.ident; + let ty = &arg.ty; + if let Some(path) = &arg.path { + let path: TokenStream = parse_str(path).unwrap(); + + if let Some(dest_type) = arg.dest_type.as_ref() { + quote! { + if type_id == std::any::TypeId::of::<#dest_type>() { + return Some(#prefix #path); + } + } + } else { + quote! { + if type_id == std::any::TypeId::of::<#ty>() { + return Some(#prefix #path); + } + } + } + } else { + quote! { + if type_id == std::any::TypeId::of::<#ty>() { + return Some(#prefix #ident); + } + } + } + }) + .collect::>() +} + +fn impl_type_uuid_provider_struct( + ty_args: &TypeArgs, + field_args: &ast::Fields, +) -> TokenStream2 { + let (field_refs, field_ref_muts) = if field_args.style == ast::Style::Unit { + (quote! {}, quote! {}) + } else { + let field_refs = create_field_components(true, &field_args.fields, field_args.style, false); + let field_ref_muts = + create_field_components(true, &field_args.fields, field_args.style, true); + (quote! { #(#field_refs)* }, quote! { #(#field_ref_muts)* }) + }; + + let ty_ident = &ty_args.ident; + let (impl_generics, ty_generics, where_clause) = ty_args.generics.split_for_impl(); + + quote! { + impl #impl_generics ComponentProvider for #ty_ident #ty_generics #where_clause { + fn query_component_ref(&self, type_id: std::any::TypeId) -> Option<&dyn std::any::Any> { + if type_id == std::any::TypeId::of::() { + return Some(self); + } + + #field_refs + + None + } + + fn query_component_mut( + &mut self, + type_id: std::any::TypeId, + ) -> Option<&mut dyn std::any::Any> { + if type_id == std::any::TypeId::of::() { + return Some(self); + } + + #field_ref_muts + + None + } + } + } +} diff --git a/fyrox-core-derive/src/lib.rs b/fyrox-core-derive/src/lib.rs index 39654930b..bc1f5d02e 100644 --- a/fyrox-core-derive/src/lib.rs +++ b/fyrox-core-derive/src/lib.rs @@ -1,3 +1,4 @@ +mod component; mod reflect; mod uuid; mod visit; @@ -53,3 +54,12 @@ pub fn type_uuid(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); TokenStream::from(uuid::impl_type_uuid_provider(ast)) } + +/// Implements `ComponentProvider` trait +/// +/// User has to import `ComponentProvider` trait to use this macro. +#[proc_macro_derive(ComponentProvider, attributes(component))] +pub fn component(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + TokenStream::from(component::impl_type_uuid_provider(ast)) +} diff --git a/fyrox-core-derive/tests/it/component.rs b/fyrox-core-derive/tests/it/component.rs new file mode 100644 index 000000000..0ed139231 --- /dev/null +++ b/fyrox-core-derive/tests/it/component.rs @@ -0,0 +1,79 @@ +use fyrox_core::type_traits::prelude::*; + +#[derive(ComponentProvider)] +pub struct Foo { + #[component(include)] + component: Component, + #[component(include)] + other_component: OtherComponent, + #[component( + include, + path = "wrapper.some_other_component", + dest_type = "SomeOtherComponent" + )] + wrapper: Wrapper, + #[allow(dead_code)] + non_component: String, +} + +#[derive(PartialEq, Debug)] +pub struct Component { + stuff: f32, +} + +#[derive(PartialEq, Debug)] +pub struct OtherComponent { + other_stuff: u32, +} + +pub struct Wrapper { + some_other_component: SomeOtherComponent, +} + +#[derive(PartialEq, Debug)] +pub struct SomeOtherComponent { + other_stuff: u8, +} + +#[test] +fn test_component_provider() { + #[allow(clippy::disallowed_names)] // Stupid fucking clippy + let mut foo = Foo { + component: Component { stuff: 123.321 }, + other_component: OtherComponent { other_stuff: 123 }, + wrapper: Wrapper { + some_other_component: SomeOtherComponent { other_stuff: 77 }, + }, + non_component: Default::default(), + }; + assert_eq!( + (&foo as &dyn ComponentProvider).component_ref::(), + Some(Component { stuff: 123.321 }).as_ref() + ); + assert_eq!( + (&foo as &dyn ComponentProvider).component_ref::(), + Some(OtherComponent { other_stuff: 123 }).as_ref() + ); + assert_eq!( + (&foo as &dyn ComponentProvider).component_ref::(), + Some(SomeOtherComponent { other_stuff: 77 }).as_ref() + ); + + assert_eq!( + (&mut foo as &mut dyn ComponentProvider).component_mut::(), + Some(Component { stuff: 123.321 }).as_mut() + ); + assert_eq!( + (&mut foo as &mut dyn ComponentProvider).component_mut::(), + Some(OtherComponent { other_stuff: 123 }).as_mut() + ); + assert_eq!( + (&mut foo as &mut dyn ComponentProvider).component_mut::(), + Some(SomeOtherComponent { other_stuff: 77 }).as_mut() + ); + + assert_eq!( + (&foo as &dyn ComponentProvider).component_ref::(), + None + ); +} diff --git a/fyrox-core-derive/tests/it/main.rs b/fyrox-core-derive/tests/it/main.rs index 21762ffc5..af2b0b057 100644 --- a/fyrox-core-derive/tests/it/main.rs +++ b/fyrox-core-derive/tests/it/main.rs @@ -5,6 +5,7 @@ // #![feature(trace_macros)] // trace_macros!(true); +pub mod component; pub mod reflect; pub mod uuid; pub mod visit; diff --git a/fyrox-core/src/type_traits.rs b/fyrox-core/src/type_traits.rs index 60fa978be..a87cc587d 100644 --- a/fyrox-core/src/type_traits.rs +++ b/fyrox-core/src/type_traits.rs @@ -1,8 +1,10 @@ +pub use fyrox_core_derive::ComponentProvider; pub use fyrox_core_derive::TypeUuidProvider; +use std::any::{Any, TypeId}; use uuid::Uuid; pub mod prelude { - pub use super::{combine_uuids, TypeUuidProvider}; + pub use super::{combine_uuids, ComponentProvider, TypeUuidProvider}; pub use uuid::{uuid, Uuid}; } @@ -67,3 +69,89 @@ pub fn combine_uuids(a: Uuid, b: Uuid) -> Uuid { Uuid::from_bytes(combined_bytes) } + +/// Component provider provides dynamic access to inner components of an object by their type id. +pub trait ComponentProvider { + /// Allows an object to provide access to inner components. + fn query_component_ref(&self, type_id: TypeId) -> Option<&dyn Any>; + + /// Allows an object to provide access to inner components. + fn query_component_mut(&mut self, type_id: TypeId) -> Option<&mut dyn Any>; +} + +impl dyn ComponentProvider { + /// Tries to borrow a component of given type. + #[inline] + pub fn component_ref(&self) -> Option<&T> { + ComponentProvider::query_component_ref(self, TypeId::of::()) + .and_then(|c| c.downcast_ref()) + } + + /// Tries to borrow a component of given type. + #[inline] + pub fn component_mut(&mut self) -> Option<&mut T> { + ComponentProvider::query_component_mut(self, TypeId::of::()) + .and_then(|c| c.downcast_mut()) + } +} + +/// Implements [`ComponentProvider::query_component_ref`] and [`ComponentProvider::query_component_mut`] in a much +/// shorter way. +#[macro_export] +macro_rules! impl_component_provider { + ($dest_type:ty) => { + impl $crate::type_traits::ComponentProvider for $dest_type { + fn query_component_ref(&self, type_id: std::any::TypeId) -> Option<&dyn std::any::Any> { + if type_id == std::any::TypeId::of::() { + return Some(self); + } + None + } + + fn query_component_mut( + &mut self, + type_id: std::any::TypeId, + ) -> Option<&mut dyn std::any::Any> { + if type_id == std::any::TypeId::of::() { + return Some(self); + } + None + } + } + }; + + ($dest_type:ty, $($($comp_field:ident).*: $comp_type:ty),*) => { + impl $crate::type_traits::ComponentProvider for $dest_type { + fn query_component_ref(&self, type_id: std::any::TypeId) -> Option<&dyn std::any::Any> { + if type_id == std::any::TypeId::of::() { + return Some(self); + } + + $( + if type_id == std::any::TypeId::of::<$comp_type>() { + return Some(&self.$($comp_field).*) + } + )* + + None + } + + fn query_component_mut( + &mut self, + type_id: std::any::TypeId, + ) -> Option<&mut dyn std::any::Any> { + if type_id == std::any::TypeId::of::() { + return Some(self); + } + + $( + if type_id == std::any::TypeId::of::<$comp_type>() { + return Some(&mut self.$($comp_field).*) + } + )* + + None + } + } + }; +} diff --git a/fyrox-scripts/src/camera.rs b/fyrox-scripts/src/camera.rs index 544d9feff..c7808f52b 100644 --- a/fyrox-scripts/src/camera.rs +++ b/fyrox-scripts/src/camera.rs @@ -1,20 +1,19 @@ //! Flying camera controller script is used to create flying cameras, that can be rotated via mouse and moved via keyboard keys. //! See [`FlyingCameraController`] docs for more info and usage examples. -use fyrox::core::uuid_provider; use fyrox::{ - core::algebra::UnitVector3, core::{ - algebra::{UnitQuaternion, Vector3}, + algebra::{UnitQuaternion, UnitVector3, Vector3}, curve::{Curve, CurveKey, CurveKeyKind}, + impl_component_provider, math::Vector3Ext, reflect::prelude::*, + uuid_provider, variable::InheritableVariable, visitor::prelude::*, }, event::{DeviceEvent, ElementState, Event, WindowEvent}, gui::{key::KeyBinding, message::KeyCode}, - impl_component_provider, script::{ScriptContext, ScriptTrait}, utils, }; diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 3a9463dbe..be7a28f6f 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -2168,9 +2168,11 @@ impl Drop for Engine { mod test { use crate::{ asset::manager::ResourceManager, - core::{pool::Handle, reflect::prelude::*, visitor::prelude::*}, - engine::ScriptProcessor, - impl_component_provider, + core::{ + impl_component_provider, pool::Handle, reflect::prelude::*, task::TaskPool, + uuid_provider, visitor::prelude::*, + }, + engine::{task::TaskPoolHandler, GraphicsContext, ScriptProcessor}, scene::{base::BaseBuilder, node::Node, pivot::PivotBuilder, Scene, SceneContainer}, script::{ Script, ScriptContext, ScriptDeinitContext, ScriptMessageContext, ScriptMessagePayload, @@ -2179,10 +2181,6 @@ mod test { }; use std::sync::Arc; - use crate::engine::task::TaskPoolHandler; - use crate::engine::GraphicsContext; - use fyrox_core::task::TaskPool; - use fyrox_core::uuid_provider; use std::sync::mpsc::{self, Sender, TryRecvError}; #[allow(clippy::enum_variant_names)] diff --git a/src/engine/task.rs b/src/engine/task.rs index b0a20bbe1..4fd968ae9 100644 --- a/src/engine/task.rs +++ b/src/engine/task.rs @@ -151,8 +151,7 @@ impl TaskPoolHandler { /// /// ```rust ,no_run /// # use fyrox::{ - /// # core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*}, - /// # impl_component_provider, + /// # core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*, impl_component_provider}, /// # resource::model::{Model, ModelResourceExtension}, /// # script::{ScriptContext, ScriptTrait}, /// # }; diff --git a/src/scene/node/mod.rs b/src/scene/node/mod.rs index ddb43480c..fde4a17e1 100644 --- a/src/scene/node/mod.rs +++ b/src/scene/node/mod.rs @@ -571,6 +571,7 @@ mod test { core::{ algebra::{Matrix4, Vector3}, futures::executor::block_on, + impl_component_provider, reflect::prelude::*, uuid::{uuid, Uuid}, variable::InheritableVariable, @@ -578,7 +579,6 @@ mod test { TypeUuidProvider, }, engine::{self, SerializationContext}, - impl_component_provider, resource::model::{Model, ModelResourceExtension}, scene::{ base::BaseBuilder, diff --git a/src/script/mod.rs b/src/script/mod.rs index e97f3e249..78559b1ce 100644 --- a/src/script/mod.rs +++ b/src/script/mod.rs @@ -2,24 +2,22 @@ //! Script is used to add custom logic to scene nodes. See [ScriptTrait] for more info. -use crate::engine::task::TaskPoolHandler; -use crate::engine::GraphicsContext; use crate::{ asset::manager::ResourceManager, core::{ log::Log, pool::Handle, reflect::{FieldInfo, Reflect, ReflectArray, ReflectList}, + type_traits::ComponentProvider, uuid::Uuid, visitor::{Visit, VisitResult, Visitor}, + TypeUuidProvider, }, - engine::ScriptMessageDispatcher, + engine::{task::TaskPoolHandler, GraphicsContext, ScriptMessageDispatcher}, event::Event, plugin::Plugin, scene::{node::Node, Scene}, - utils::component::ComponentProvider, }; -use fyrox_core::TypeUuidProvider; use std::{ any::{Any, TypeId}, fmt::{Debug, Formatter}, @@ -185,10 +183,10 @@ pub trait BaseScript: Visit + Reflect + Send + Debug + 'static { /// core::uuid::Uuid, /// script::ScriptTrait, /// core::TypeUuidProvider, - /// core::uuid::uuid, impl_component_provider + /// core::uuid::uuid, core::type_traits::prelude::* /// }; /// - /// #[derive(Reflect, Visit, Debug, Clone)] + /// #[derive(Reflect, Visit, Debug, Clone, ComponentProvider)] /// struct MyScript { } /// /// // Implement TypeUuidProvider trait that will return type uuid of the type. @@ -201,8 +199,6 @@ pub trait BaseScript: Visit + Reflect + Send + Debug + 'static { /// } /// } /// - /// impl_component_provider!(MyScript); - /// /// impl ScriptTrait for MyScript { } /// ``` fn id(&self) -> Uuid; @@ -388,8 +384,7 @@ pub trait ScriptTrait: BaseScript + ComponentProvider { /// /// ```rust /// use fyrox::{ - /// core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*}, - /// impl_component_provider, + /// core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*, type_traits::prelude::*}, /// core::TypeUuidProvider, /// script::ScriptTrait, /// script::{ScriptContext, ScriptMessageContext, ScriptMessagePayload}, @@ -397,7 +392,7 @@ pub trait ScriptTrait: BaseScript + ComponentProvider { /// /// struct Message; /// - /// #[derive(Reflect, Visit, Debug, Clone)] + /// #[derive(Reflect, Visit, Debug, Clone, ComponentProvider)] /// struct MyScript {} /// /// # impl TypeUuidProvider for MyScript { @@ -406,8 +401,6 @@ pub trait ScriptTrait: BaseScript + ComponentProvider { /// # } /// # } /// - /// # impl_component_provider!(MyScript); - /// /// impl ScriptTrait for MyScript { /// fn on_start(&mut self, ctx: &mut ScriptContext) { /// // Subscription is mandatory to receive any message of the type! @@ -612,10 +605,9 @@ impl Script { mod test { use crate::{ core::{ - reflect::prelude::*, variable::try_inherit_properties, variable::InheritableVariable, - visitor::prelude::*, + impl_component_provider, reflect::prelude::*, variable::try_inherit_properties, + variable::InheritableVariable, visitor::prelude::*, }, - impl_component_provider, scene::base::Base, script::{Script, ScriptTrait}, }; diff --git a/src/utils/component.rs b/src/utils/component.rs deleted file mode 100644 index 4497b9099..000000000 --- a/src/utils/component.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Component provider provides dynamic access to inner components of an object by their type id. - -use std::any::{Any, TypeId}; - -/// Component provider provides dynamic access to inner components of an object by their type id. -pub trait ComponentProvider { - /// Allows an object to provide access to inner components. - fn query_component_ref(&self, type_id: TypeId) -> Option<&dyn Any>; - - /// Allows an object to provide access to inner components. - fn query_component_mut(&mut self, type_id: TypeId) -> Option<&mut dyn Any>; -} - -/// Implements [`ComponentProvider::query_component_ref`] and [`ComponentProvider::query_component_mut`] in a much -/// shorter way. -#[macro_export] -macro_rules! impl_component_provider { - ($dest_type:ty) => { - impl $crate::utils::component::ComponentProvider for $dest_type { - fn query_component_ref(&self, type_id: std::any::TypeId) -> Option<&dyn std::any::Any> { - if type_id == std::any::TypeId::of::() { - return Some(self); - } - None - } - - fn query_component_mut( - &mut self, - type_id: std::any::TypeId, - ) -> Option<&mut dyn std::any::Any> { - if type_id == std::any::TypeId::of::() { - return Some(self); - } - None - } - } - }; - - ($dest_type:ty, $($($comp_field:ident).*: $comp_type:ty),*) => { - impl $crate::utils::component::ComponentProvider for $dest_type { - fn query_component_ref(&self, type_id: std::any::TypeId) -> Option<&dyn std::any::Any> { - if type_id == std::any::TypeId::of::() { - return Some(self); - } - - $( - if type_id == std::any::TypeId::of::<$comp_type>() { - return Some(&self.$($comp_field).*) - } - )* - - None - } - - fn query_component_mut( - &mut self, - type_id: std::any::TypeId, - ) -> Option<&mut dyn std::any::Any> { - if type_id == std::any::TypeId::of::() { - return Some(self); - } - - $( - if type_id == std::any::TypeId::of::<$comp_type>() { - return Some(&mut self.$($comp_field).*) - } - )* - - None - } - } - }; -} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 5d3f1f160..93970f1db 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,7 +4,6 @@ pub mod astar; pub mod behavior; -pub mod component; pub mod lightmap; pub mod navmesh; pub mod raw_mesh;