specs-mirror 0.4.1

A storage for use with specs to help synchronize with an external library.
Documentation
extern crate shrev;
extern crate specs;
extern crate specs_mirror;

use shrev::{EventChannel, ReaderId};
use specs::prelude::*;
use specs::world::Index;
use specs_mirror::*;

use std::collections::HashMap;
use std::sync::Arc;

#[derive(Clone, Debug, PartialEq, Eq)]
struct Comp(Arc<str>);

impl Component for Comp {
    type Storage = MirroredStorage<Self>;
}

enum CompEvent {
    Inserted(Index, Comp),
    Removed(Index, Comp),
}

impl Mirrored for Comp {
    type Event = CompEvent;

    fn insert(&mut self, chan: &mut EventChannel<Self::Event>, id: Index) {
        chan.single_write(CompEvent::Inserted(id, self.clone()));
    }

    fn remove(&mut self, chan: &mut EventChannel<Self::Event>, id: Index) {
        chan.single_write(CompEvent::Removed(id, self.clone()));
    }
}

impl Comp {
    fn set(&mut self, chan: &mut EventChannel<CompEvent>, entity: Entity, state: Comp) {
        chan.single_write(CompEvent::Removed(entity.id(), self.clone()));
        *self = state;
        chan.single_write(CompEvent::Inserted(entity.id(), self.clone()));
    }
}

struct CompSystem {
    reader: ReaderId<CompEvent>,
    store: HashMap<Index, Comp>,
}

impl CompSystem {
    fn new(mut store: WriteStorage<Comp>) -> Self {
        let reader = store.register_reader();
        let store = HashMap::new();
        CompSystem { reader, store }
    }
}

impl<'a> System<'a> for CompSystem {
    type SystemData = ReadStorage<'a, Comp>;

    fn run(&mut self, comp: Self::SystemData) {
        for event in comp.read_events(&mut self.reader) {
            match *event {
                CompEvent::Inserted(id, ref data) => {
                    assert!(self.store.insert(id, data.clone()).is_none())
                }
                CompEvent::Removed(id, ref data) => {
                    assert_eq!(self.store.remove(&id), Some(data.clone()))
                }
            }
        }
    }
}

const ACTIONS: usize = 4;

fn modify(comps: &mut WriteStorage<Comp>, ent: Entity, i: usize) {
    match i % ACTIONS {
        0 => {
            comps
                .insert(ent, Comp(Arc::from(ent.id().to_string())))
                .unwrap();
        }
        1 => {
            comps.remove(ent);
        }
        3 => {
            if let Some((comp, chan)) = comps.modify(ent) {
                comp.set(chan, ent, Comp(Arc::from(ent.id().to_string())));
            }
        }
        _ => (),
    }
}

fn test_synced(ents: Entities, left: ReadStorage<Comp>, right: &HashMap<Index, Comp>) {
    let left: HashMap<Index, Comp> = (&*ents, &left)
        .join()
        .map(|(ent, comp)| (ent.id(), comp.clone()))
        .collect();
    assert_eq!(&left, right);
}

#[test]
fn test() {
    const N: u32 = 6;

    let mut world = World::new();
    world.register::<Comp>();

    let mut sys = CompSystem::new(world.write_storage::<Comp>());

    let entities: Vec<Entity> = world.create_iter().take(ACTIONS.pow(N)).collect();

    sys.run_now(&mut world.res);
    test_synced(world.entities(), world.read_storage::<Comp>(), &sys.store);

    {
        let mut comps = world.write_storage::<Comp>();
        for (mut i, &ent) in entities.iter().enumerate() {
            for _ in 0..N {
                modify(&mut comps, ent, i);
                i /= ACTIONS;
            }
            assert_eq!(i, 0);
        }
    }

    sys.run_now(&mut world.res);
    test_synced(world.entities(), world.read_storage::<Comp>(), &sys.store);

    {
        let mut comps = world.write_storage::<Comp>();
        for (mut i, &ent) in entities.iter().rev().enumerate() {
            for _ in 0..N {
                modify(&mut comps, ent, i);
                i /= ACTIONS;
            }
            assert_eq!(i, 0);
        }
    }

    sys.run_now(&mut world.res);
    test_synced(world.entities(), world.read_storage::<Comp>(), &sys.store);
}