use std::any::TypeId;
use std::collections::HashMap;
use std::fmt;
use std::mem::size_of;
use crate::engine::error::{RegistryError, RegistryResult};
use crate::engine::storage::{Attribute, TypeErasedAttribute};
use crate::engine::types::{ComponentID, COMPONENT_CAP};
use super::descriptor::ComponentDesc;
#[cfg(feature = "gpu")]
use super::global::GPUPod;
pub type FactoryFn = fn() -> Box<dyn TypeErasedAttribute>;
fn new_attribute_storage<T: 'static + Send + Sync>() -> Box<dyn TypeErasedAttribute> {
Box::new(Attribute::<T>::default())
}
pub struct ComponentRegistry {
next_id: ComponentID,
by_type: HashMap<TypeId, ComponentID>,
pub(crate) by_id: Vec<Option<ComponentDesc>>,
pub(crate) factories: Vec<Option<FactoryFn>>,
frozen: bool,
}
impl ComponentRegistry {
pub fn new() -> Self {
Self {
next_id: 0,
by_type: HashMap::new(),
by_id: vec![None; COMPONENT_CAP],
factories: vec![None; COMPONENT_CAP],
frozen: false,
}
}
fn alloc_id(&mut self) -> Result<ComponentID, RegistryError> {
let component_id = self.next_id;
if (component_id as usize) >= COMPONENT_CAP {
return Err(RegistryError::CapacityExceeded { cap: COMPONENT_CAP });
}
self.next_id = component_id.wrapping_add(1);
Ok(component_id)
}
pub fn freeze(&mut self) {
self.frozen = true;
}
pub fn is_frozen(&self) -> bool {
self.frozen
}
pub fn component_id_of_type_id(&self, type_id: TypeId) -> Option<ComponentID> {
self.by_type.get(&type_id).copied()
}
pub fn description_by_component_id(&self, component_id: ComponentID) -> Option<&ComponentDesc> {
self.by_id
.get(component_id as usize)
.and_then(|o| o.as_ref())
}
pub fn require_component_id(&self, component_id: ComponentID) -> Result<(), RegistryError> {
let index = component_id as usize;
if index >= COMPONENT_CAP {
return Err(RegistryError::InvalidComponentId {
component_id,
cap: COMPONENT_CAP,
});
}
if self.by_id[index].is_none() {
return Err(RegistryError::ComponentIdNotRegistered { component_id });
}
Ok(())
}
pub fn get_factory(&self, component_id: ComponentID) -> Option<FactoryFn> {
self.factories.get(component_id as usize).and_then(|o| *o)
}
pub fn make_empty_component(
&self,
component_id: ComponentID,
) -> RegistryResult<Box<dyn TypeErasedAttribute>> {
let factory = self
.get_factory(component_id)
.ok_or(RegistryError::MissingFactory { component_id })?;
Ok(factory())
}
pub fn register<T: 'static + Send + Sync>(&mut self) -> Result<ComponentID, RegistryError> {
let type_id = TypeId::of::<T>();
if let Some(&existing) = self.by_type.get(&type_id) {
return Ok(existing);
}
if size_of::<T>() == 0 {
return Err(RegistryError::ZeroSizedComponent { type_id });
}
if self.frozen {
return Err(RegistryError::Frozen);
}
let id = self.alloc_id()?;
self.by_type.insert(type_id, id);
self.by_id[id as usize] = Some(ComponentDesc::of::<T>().with_id(id));
self.factories[id as usize] = Some(new_attribute_storage::<T>);
Ok(id)
}
#[cfg(feature = "gpu")]
pub fn register_gpu<T: GPUPod + 'static + Send + Sync>(
&mut self,
) -> Result<ComponentID, RegistryError> {
let id = self.register::<T>()?;
if let Some(desc) = self.by_id[id as usize].as_mut() {
desc.gpu_usage = true;
}
Ok(id)
}
#[cfg(feature = "gpu")]
pub fn mark_gpu_safe(&mut self, component_id: ComponentID) -> Result<(), RegistryError> {
match self.by_id.get_mut(component_id as usize) {
Some(Some(desc)) => {
desc.gpu_usage = true;
Ok(())
}
_ => Err(RegistryError::NotRegistered {
type_id: TypeId::of::<()>(),
}),
}
}
pub fn id_of<T: 'static>(&self) -> Option<ComponentID> {
self.component_id_of_type_id(TypeId::of::<T>())
}
pub fn require_id_of<T: 'static>(&self) -> Result<ComponentID, RegistryError> {
self.id_of::<T>().ok_or(RegistryError::NotRegistered {
type_id: TypeId::of::<T>(),
})
}
}
impl Default for ComponentRegistry {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for ComponentRegistry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ComponentRegistry")
.field("next_id", &self.next_id)
.field("frozen", &self.frozen)
.field("registered_count", &self.by_type.len())
.finish()
}
}