use std::any::{type_name, Any, TypeId}; use std::fmt::Debug; use seq_macro::seq; use crate::lock::WriteGuard; use crate::system::{ComponentRefMut, Input as SystemInput}; use crate::type_name::TypeName; use crate::{EntityComponent, WorldData}; pub mod local; pub(crate) mod storage; pub trait Component: SystemInput + Any + TypeName { /// The component type in question. Will usually be `Self` type Component: Component where Self: Sized; type RefMut<'component> where Self: Sized; /// Returns the ID of this component's type. fn id(&self) -> Id; #[doc(hidden)] fn as_any_mut(&mut self) -> &mut dyn Any; #[doc(hidden)] fn as_any(&self) -> &dyn Any; fn is_optional(&self) -> bool { false } fn prepare(_world_data: &WorldData) where Self: Sized, { } } impl dyn Component { pub fn downcast_mut(&mut self) -> Option<&mut Real> { self.as_any_mut().downcast_mut() } pub fn downcast_ref(&self) -> Option<&Real> { self.as_any().downcast_ref() } pub fn is(&self) -> bool { self.as_any().is::() } } impl Debug for dyn Component { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { formatter.debug_struct("Component").finish_non_exhaustive() } } impl TypeName for Box { fn type_name(&self) -> &'static str { self.as_ref().type_name() } } impl Component for Option where ComponentT: Component, { type Component = ComponentT; type RefMut<'component> = Option>; fn id(&self) -> Id { Id::of::() } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn as_any(&self) -> &dyn Any { self } fn is_optional(&self) -> bool { true } } impl TypeName for Option where ComponentT: Component, { fn type_name(&self) -> &'static str { type_name::() } } impl SystemInput for Option where ComponentT: Component {} /// The ID of a [`Component`] type. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Id { inner: TypeId, } impl Id { #[must_use] pub fn of() -> Self where ComponentT: Component, { Self { inner: TypeId::of::() } } } /// A sequence of components. pub trait Sequence { type Refs<'component> where Self: 'component; fn into_vec(self) -> Vec>; fn metadata() -> Vec; fn from_components<'component>( components: impl Iterator, ) -> Self::Refs<'component>; fn prepare(_world_data: &WorldData); } /// [`Component`] metadata. #[derive(Debug, Clone)] #[non_exhaustive] pub struct Metadata { pub id: Id, pub is_optional: IsOptional, } impl Metadata { pub fn of(component: &ComponentT) -> Self { Self { id: component.id(), is_optional: component.is_optional().into(), } } } /// Whether or not a `Component` is optional. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IsOptional { Yes, No, } impl From for IsOptional { fn from(is_optional: bool) -> Self { if is_optional { return IsOptional::Yes; } IsOptional::No } } /// Returns whether the given component type is a optional component. /// /// Will return `true` if the component is a [`Option`]. #[must_use] pub fn is_optional() -> bool { if Id::of::() == Id::of::>() { return true; } false } pub trait FromOptional<'comp> { fn from_optional_component( optional_component: Option>>, ) -> Self; } macro_rules! inner { ($c: tt) => { seq!(I in 0..=$c { impl<#(Comp~I: Component,)*> Sequence for (#(Comp~I,)*) where #(for<'comp> Comp~I::RefMut<'comp>: FromOptional<'comp>,)* { type Refs<'component> = (#(Comp~I::RefMut<'component>,)*) where Self: 'component; fn into_vec(self) -> Vec> { Vec::from_iter([#(Box::new(self.I) as Box,)*]) } fn metadata() -> Vec { vec![ #( Metadata { id: Id::of::(), is_optional: is_optional::().into() }, )* ] } fn from_components<'component>( components: impl Iterator, ) -> Self::Refs<'component> { #( let mut comp_~I: Option>> = None; )* for comp in components { #( if comp.id == Id::of::() { comp_~I = Some(lock_component(comp)); continue; } )* } (#( Comp~I::RefMut::from_optional_component(comp_~I), )*) } fn prepare(world_data: &WorldData) { #( Comp~I::prepare(world_data); )* } } }); }; } seq!(C in 0..=64 { inner!(C); }); fn lock_component( entity_component: &EntityComponent, ) -> WriteGuard<'_, Box> { entity_component .component .write_nonblock() .unwrap_or_else(|_| { panic!( "Failed to acquire read-write lock to component {}", entity_component.name ); }) }