safe_ecs 0.1.0

ECS written in safe code
use std::sync::atomic::{AtomicUsize, Ordering};

#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct Entity(pub(crate) usize);

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct EntityMeta {
    pub(crate) archetype: usize,
}

#[derive(Debug)]
pub(crate) struct Entities {
    len: AtomicUsize,
    meta: Vec<Option<EntityMeta>>,
}

impl Entities {
    pub fn new() -> Self {
        Self {
            len: AtomicUsize::new(0),
            meta: vec![],
        }
    }

    pub(crate) fn fix_reserved_entities(
        &mut self,
        mut do_archetype_stuf: impl FnMut(Entity),
    ) -> NoReservedEntities<'_> {
        let new_len = *self.len.get_mut();
        for id in self.meta.len()..new_len {
            do_archetype_stuf(Entity(id));
        }
        self.meta.resize(new_len, Some(EntityMeta { archetype: 0 }));
        NoReservedEntities(self)
    }

    pub fn reserve_entity(&self) -> Entity {
        let id = self.len.fetch_add(1, Ordering::Relaxed);
        if let usize::MAX = id {
            panic!("too many entities spawned (> usize::MAX)");
        }
        Entity(id)
    }

    pub fn is_alive(&self, entity: Entity) -> bool {
        self.meta
            .get(entity.0)
            .map(|meta| meta.is_some())
            .unwrap_or(false)
    }

    pub fn meta(&self, entity: Entity) -> Option<&EntityMeta> {
        self.meta.get(entity.0).and_then(Option::as_ref)
    }

    pub(crate) fn meta_mut(&mut self, entity: Entity) -> Option<&mut EntityMeta> {
        self.meta.get_mut(entity.0).and_then(Option::as_mut)
    }

    pub fn spawn(&mut self, mut do_archetype_stuff: impl FnMut(Entity)) -> Entity {
        let e = self.reserve_entity();
        self.fix_reserved_entities(&mut do_archetype_stuff);
        e
    }
}

pub(crate) struct NoReservedEntities<'a>(&'a mut Entities);

impl<'a> NoReservedEntities<'a> {
    pub fn despawn(&mut self, entity: Entity, handle_despawn: impl FnOnce(EntityMeta)) {
        if self.0.is_alive(entity) {
            handle_despawn(self.0.meta[entity.0].unwrap());
            self.0.meta[entity.0] = None;
        }
    }
}