moved ComponentProvider trait to fyrox_code + added proc macro for it

This commit is contained in:
Dmitry Stepanov
2024-01-09 19:08:30 +03:00
parent 85d264c3e2
commit a78daea34d
12 changed files with 337 additions and 106 deletions
+139
View File
@@ -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<VariantArgs, FieldArgs>,
}
#[derive(FromField, Clone)]
#[darling(attributes(component))]
pub struct FieldArgs {
pub ident: Option<Ident>,
pub ty: Type,
#[darling(default)]
pub include: bool,
#[darling(default)]
pub path: Option<String>,
#[darling(default)]
pub dest_type: Option<Type>,
}
#[derive(FromVariant)]
#[darling(attributes(component))]
pub struct VariantArgs {
pub ident: Ident,
pub fields: ast::Fields<FieldArgs>,
}
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<TokenStream2> {
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::<Vec<_>>()
}
fn impl_type_uuid_provider_struct(
ty_args: &TypeArgs,
field_args: &ast::Fields<FieldArgs>,
) -> 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::<Self>() {
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::<Self>() {
return Some(self);
}
#field_ref_muts
None
}
}
}
}
+10
View File
@@ -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))
}
+79
View File
@@ -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::<Component>(),
Some(Component { stuff: 123.321 }).as_ref()
);
assert_eq!(
(&foo as &dyn ComponentProvider).component_ref::<OtherComponent>(),
Some(OtherComponent { other_stuff: 123 }).as_ref()
);
assert_eq!(
(&foo as &dyn ComponentProvider).component_ref::<SomeOtherComponent>(),
Some(SomeOtherComponent { other_stuff: 77 }).as_ref()
);
assert_eq!(
(&mut foo as &mut dyn ComponentProvider).component_mut::<Component>(),
Some(Component { stuff: 123.321 }).as_mut()
);
assert_eq!(
(&mut foo as &mut dyn ComponentProvider).component_mut::<OtherComponent>(),
Some(OtherComponent { other_stuff: 123 }).as_mut()
);
assert_eq!(
(&mut foo as &mut dyn ComponentProvider).component_mut::<SomeOtherComponent>(),
Some(SomeOtherComponent { other_stuff: 77 }).as_mut()
);
assert_eq!(
(&foo as &dyn ComponentProvider).component_ref::<String>(),
None
);
}
+1
View File
@@ -5,6 +5,7 @@
// #![feature(trace_macros)]
// trace_macros!(true);
pub mod component;
pub mod reflect;
pub mod uuid;
pub mod visit;
+89 -1
View File
@@ -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<T: Any>(&self) -> Option<&T> {
ComponentProvider::query_component_ref(self, TypeId::of::<T>())
.and_then(|c| c.downcast_ref())
}
/// Tries to borrow a component of given type.
#[inline]
pub fn component_mut<T: Any>(&mut self) -> Option<&mut T> {
ComponentProvider::query_component_mut(self, TypeId::of::<T>())
.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::<Self>() {
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::<Self>() {
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::<Self>() {
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::<Self>() {
return Some(self);
}
$(
if type_id == std::any::TypeId::of::<$comp_type>() {
return Some(&mut self.$($comp_field).*)
}
)*
None
}
}
};
}
+3 -4
View File
@@ -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,
};
+5 -7
View File
@@ -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)]
+1 -2
View File
@@ -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},
/// # };
+1 -1
View File
@@ -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,
+9 -17
View File
@@ -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},
};
-73
View File
@@ -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::<Self>() {
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::<Self>() {
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::<Self>() {
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::<Self>() {
return Some(self);
}
$(
if type_id == std::any::TypeId::of::<$comp_type>() {
return Some(&mut self.$($comp_field).*)
}
)*
None
}
}
};
}
-1
View File
@@ -4,7 +4,6 @@
pub mod astar;
pub mod behavior;
pub mod component;
pub mod lightmap;
pub mod navmesh;
pub mod raw_mesh;