safe_ecs 0.1.0

ECS written in safe code
use std::marker::PhantomData;

use crate::{Component, Entity, World};

pub trait Command: 'static {
    fn apply(self: Box<Self>, world: &mut World);
}

struct RemoveCmd<T: Component>(Entity, PhantomData<T>);
impl<T: Component> Command for RemoveCmd<T> {
    fn apply(self: Box<Self>, world: &mut World) {
        world.remove_component::<T>(self.0);
    }
}
struct InsertCmd<T: Component>(Entity, T);
impl<T: Component> Command for InsertCmd<T> {
    fn apply(self: Box<Self>, world: &mut World) {
        world.insert_component(self.0, self.1);
    }
}

pub struct CommandBuffer(Vec<Box<dyn Command>>);
impl CommandBuffer {
    pub fn new() -> Self {
        Self(vec![])
    }

    pub fn apply(&mut self, world: &mut World) {
        world
            .entities
            .fix_reserved_entities(|reserved| world.archetypes[0].entities.push(reserved));
        for cmd in self.0.drain(..) {
            cmd.apply(world);
        }
    }
}
pub struct Commands<'a>(pub(crate) &'a mut CommandBuffer, pub(crate) &'a World);
pub struct CommandsWithEntity<'a, 'b>(&'a mut Commands<'b>, Entity);

impl<'a> Commands<'a> {
    pub fn entity(&mut self, entity: Entity) -> CommandsWithEntity<'_, 'a> {
        CommandsWithEntity(self, entity)
    }

    pub fn remove_component<T: Component>(&mut self, entity: Entity) -> &mut Self {
        self.0
             .0
            .push(Box::new(RemoveCmd::<T>(entity, PhantomData)));
        self
    }

    pub fn insert_component<T: Component>(&mut self, entity: Entity, component: T) -> &mut Self {
        self.0 .0.push(Box::new(InsertCmd::<T>(entity, component)));
        self
    }

    pub fn spawn(&mut self) -> CommandsWithEntity<'_, 'a> {
        let e = self.1.entities.reserve_entity();
        CommandsWithEntity(self, e)
    }
}

impl CommandsWithEntity<'_, '_> {
    pub fn remove<T: Component>(&mut self) -> &mut Self {
        self.0.remove_component::<T>(self.1);
        self
    }

    pub fn insert<T: Component>(&mut self, component: T) -> &mut Self {
        self.0.insert_component::<T>(self.1, component);
        self
    }

    pub fn id(&mut self) -> Entity {
        self.1
    }
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[test]
    fn basic_insert() {
        let mut world = World::new();
        let e = world.spawn().id();
        world.access_scope(|mut cmds: Commands| {
            cmds.entity(e).insert(10_u32).insert(12_u64).remove::<u32>();
        });
        let mut q = world.query::<&u32>().unwrap();
        let mut iter = q.iter_mut();
        assert_eq!(iter.next(), None);
        let mut q = world.query::<&u64>().unwrap();
        let mut iter = q.iter_mut();
        assert_eq!(iter.next(), Some(&12));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn spawn() {
        let mut world = World::new();
        let e1 = world.access_scope(|mut cmds: Commands| {
            cmds.spawn()
                .insert(10_u32)
                .insert(12_u64)
                .remove::<u32>()
                .id()
        });

        let mut q = world.query::<(Entity, &u32)>().unwrap();
        let mut iter = q.iter_mut();
        assert_eq!(iter.next(), None);
        let mut q = world.query::<(Entity, &u64)>().unwrap();
        let mut iter = q.iter_mut();
        assert_eq!(iter.next(), Some((e1, &12)));
        assert_eq!(iter.next(), None);
    }
}