shiv 0.1.0-alpha.10

A simple modern Entity Component System
Documentation
use std::{
    alloc::Layout,
    any::{self, TypeId},
    borrow::Cow,
};

use crate::{
    hash_map::HashMap,
    storage::{ComponentStorage, DenseStorage, Resource, StorageSet, StorageType, Storages},
};

pub use shiv_macro::Component;

pub trait Component: Send + Sync + 'static {
    type Storage: Storage;
}

pub trait Storage: ComponentStorage + Sized {
    const STORAGE_TYPE: StorageType;

    fn get(storage: &Storages) -> &StorageSet<Self>;
    fn get_mut(storage: &mut Storages) -> &mut StorageSet<Self>;
}

impl Storage for DenseStorage {
    const STORAGE_TYPE: StorageType = StorageType::Dense;

    #[inline]
    fn get(storage: &Storages) -> &StorageSet<Self> {
        &storage.dense
    }

    #[inline]
    fn get_mut(storage: &mut Storages) -> &mut StorageSet<Self> {
        &mut storage.dense
    }
}

#[derive(Clone, Debug)]
pub struct ComponentDescriptor {
    name: Cow<'static, str>,
    storage_type: StorageType,
    layout: Layout,
    drop: Option<unsafe fn(*mut u8)>,
}

impl ComponentDescriptor {
    #[inline]
    pub fn new<T: Component>() -> Self {
        Self {
            name: Cow::Borrowed(any::type_name::<T>()),
            storage_type: T::Storage::STORAGE_TYPE,
            layout: Layout::new::<T>(),
            drop: Some(|ptr| unsafe { std::ptr::drop_in_place(ptr as *mut T) }),
        }
    }

    #[inline]
    pub fn new_resource<T: Resource>() -> Self {
        Self {
            name: Cow::Borrowed(any::type_name::<T>()),
            storage_type: StorageType::Dense,
            layout: Layout::new::<T>(),
            drop: Some(|ptr| unsafe { std::ptr::drop_in_place(ptr as *mut T) }),
        }
    }

    #[inline]
    pub fn name(&self) -> &str {
        self.name.as_ref()
    }

    #[inline]
    pub const fn storage_type(&self) -> StorageType {
        self.storage_type
    }

    #[inline]
    pub const fn layout(&self) -> Layout {
        self.layout
    }

    #[inline]
    pub const fn drop(&self) -> Option<unsafe fn(*mut u8)> {
        self.drop
    }
}

#[derive(Clone, Debug)]
pub struct ComponentInfo {
    pub id: ComponentId,
    pub descriptor: ComponentDescriptor,
}

impl ComponentInfo {
    #[inline]
    pub const fn id(&self) -> ComponentId {
        self.id
    }

    #[inline]
    pub const fn descriptor(&self) -> &ComponentDescriptor {
        &self.descriptor
    }

    #[inline]
    pub fn name(&self) -> &str {
        self.descriptor.name()
    }

    #[inline]
    pub const fn storage_type(&self) -> StorageType {
        self.descriptor.storage_type()
    }

    #[inline]
    pub const fn layout(&self) -> Layout {
        self.descriptor.layout
    }

    #[inline]
    pub const fn drop(&self) -> Option<unsafe fn(*mut u8)> {
        self.descriptor.drop
    }

    #[inline]
    fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
        Self { id, descriptor }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ComponentId(usize);

impl ComponentId {
    #[inline]
    pub const fn new(index: usize) -> ComponentId {
        ComponentId(index)
    }

    #[inline]
    pub fn index(self) -> usize {
        self.0
    }
}

impl Into<usize> for ComponentId {
    #[inline]
    fn into(self) -> usize {
        self.0
    }
}

impl From<usize> for ComponentId {
    #[inline]
    fn from(index: usize) -> ComponentId {
        ComponentId(index)
    }
}

#[derive(Clone, Debug, Default)]
pub struct Components {
    components: Vec<ComponentInfo>,
    indices: HashMap<TypeId, usize>,
    resource_indices: HashMap<TypeId, usize>,
}

impl Components {
    #[inline]
    pub fn new() -> Self {
        Self::default()
    }

    #[inline]
    pub fn init_component<T: Component>(&mut self) -> ComponentId {
        let type_id = TypeId::of::<T>();

        if let Some(index) = self.indices.get(&type_id) {
            return ComponentId::new(*index);
        }

        let index = self.components.len();
        self.components.push(ComponentInfo::new(
            ComponentId::new(index),
            ComponentDescriptor::new::<T>(),
        ));
        self.indices.insert(type_id, index);

        ComponentId::new(index)
    }

    #[inline]
    pub fn init_resource<T: Resource>(&mut self) -> ComponentId {
        let type_id = TypeId::of::<T>();

        if let Some(index) = self.resource_indices.get(&type_id) {
            return ComponentId::new(*index);
        }

        let index = self.components.len();
        self.components.push(ComponentInfo::new(
            ComponentId::new(index),
            ComponentDescriptor::new_resource::<T>(),
        ));
        self.resource_indices.insert(type_id, index);

        ComponentId::new(index)
    }

    #[inline]
    pub fn get_component<T: Component>(&self) -> Option<ComponentId> {
        let type_id = TypeId::of::<T>();

        if let Some(index) = self.indices.get(&type_id) {
            Some(ComponentId::new(*index))
        } else {
            None
        }
    }

    #[inline]
    pub fn get_resource<T: Resource>(&self) -> Option<ComponentId> {
        let type_id = TypeId::of::<T>();

        if let Some(index) = self.resource_indices.get(&type_id) {
            Some(ComponentId::new(*index))
        } else {
            None
        }
    }

    #[inline]
    pub fn contains_component<T: Component>(&self) -> bool {
        self.indices.contains_key(&TypeId::of::<T>())
    }

    #[inline]
    pub fn contains_resource<T: Resource>(&self) -> bool {
        self.resource_indices.contains_key(&TypeId::of::<T>())
    }

    #[inline]
    pub fn get(&self, id: ComponentId) -> Option<&ComponentInfo> {
        self.components.get(id.index())
    }

    #[inline]
    pub unsafe fn get_unchecked(&self, id: ComponentId) -> &ComponentInfo {
        unsafe { self.components.get_unchecked(id.index()) }
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.components.len()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.components.is_empty()
    }
}