use crate::Entity;
use std::{
any::{Any, TypeId},
collections::HashMap,
fmt::Debug,
ops::{Index, IndexMut},
};
pub trait Component: 'static + Sized + Debug {}
#[doc(hidden)]
pub use dirk_proc::Component;
#[doc(hidden)]
pub(crate) trait AnyComponent: Any + Debug + 'static {
fn as_any(&self) -> &dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn new_storage(&self) -> Box<dyn AnyStorage>;
fn component_type_id(&self) -> TypeId;
}
impl<T: Component> AnyComponent for T {
fn as_any(&self) -> &dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
fn new_storage(&self) -> Box<dyn AnyStorage> {
Box::new(ComponentStorage::<T>::default())
}
fn component_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
}
pub(crate) trait AnyStorage {
fn remove(&mut self, entity: Entity) -> Option<Box<dyn AnyComponent>>;
fn insert(
&mut self,
entity: Entity,
component: Box<dyn AnyComponent>,
) -> Option<Box<dyn AnyComponent>>;
fn get(&self, entity: Entity) -> Option<&dyn AnyComponent>;
fn contains(&self, entity: Entity) -> bool;
fn as_any(&self) -> &dyn Any;
}
struct ComponentStorage<C: Component> {
map: HashMap<Entity, C>,
}
impl<C: Component> Default for ComponentStorage<C> {
fn default() -> Self {
Self {
map: HashMap::new(),
}
}
}
impl<C: Component> ComponentStorage<C> {
fn get(&self, entity: Entity) -> Option<&C> {
self.map.get(&entity)
}
}
impl<C: Component> AnyStorage for ComponentStorage<C> {
fn remove(&mut self, entity: Entity) -> Option<Box<dyn AnyComponent>> {
self.map
.remove(&entity)
.map(|c| Box::new(c) as Box<dyn AnyComponent>)
}
fn insert(
&mut self,
entity: Entity,
component: Box<dyn AnyComponent>,
) -> Option<Box<dyn AnyComponent>> {
if let Ok(c) = component.into_any().downcast::<C>() {
self.map
.insert(entity, *c)
.map(|component| Box::new(component) as Box<dyn AnyComponent>)
} else {
debug_assert!(false, "insert_any called with wrong component type");
None
}
}
fn contains(&self, entity: Entity) -> bool {
self.map.contains_key(&entity)
}
fn as_any(&self) -> &dyn Any {
self
}
fn get(&self, entity: Entity) -> Option<&dyn AnyComponent> {
self.map.get(&entity).map(|c| c as &dyn AnyComponent)
}
}
impl<C: Component> IndexMut<Entity> for ComponentStorage<C> {
fn index_mut(&mut self, index: Entity) -> &mut Self::Output {
self.map
.get_mut(&index)
.expect("entity should have component if indexing")
}
}
impl<C: Component> Index<Entity> for ComponentStorage<C> {
type Output = C;
fn index(&self, index: Entity) -> &Self::Output {
&self.map[&index]
}
}
#[derive(Default)]
pub(crate) struct Components {
storages: HashMap<TypeId, Box<dyn AnyStorage>>,
}
impl std::fmt::Debug for Components {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "EntityComponents {{ {} type(s) }}", self.storages.len())
}
}
impl Components {
pub fn insert_any(
&mut self,
entity: Entity,
component: Box<dyn AnyComponent>,
) -> Option<Box<dyn AnyComponent>> {
self.storages
.entry(component.component_type_id())
.or_insert_with(|| component.new_storage())
.insert(entity, component)
}
pub fn get_any(&self, entity: Entity, type_id: TypeId) -> Option<&dyn AnyComponent> {
self.storages.get(&type_id)?.get(entity)
}
pub fn get_all(&self, entity: Entity) -> impl Iterator<Item = (TypeId, &dyn AnyComponent)> {
self.storages.iter().filter_map(move |(type_id, storage)| {
storage.get(entity).map(|component| (*type_id, component))
})
}
pub fn get<C: Component>(&self, entity: Entity) -> Option<&C> {
self.storages
.get(&TypeId::of::<C>())
.and_then(|b| b.as_any().downcast_ref::<ComponentStorage<C>>())?
.get(entity)
}
pub fn remove_any(&mut self, entity: Entity, type_id: TypeId) -> Option<Box<dyn AnyComponent>> {
self.storages
.get_mut(&type_id)
.and_then(|b| b.remove(entity))
}
pub fn contains(&self, entity: Entity, type_id: TypeId) -> bool {
self.storages
.get(&type_id)
.is_some_and(|s| s.contains(entity))
}
}