use crate::no_such_entity::NoSuchEntity;
use crate::Entities;
use crate::Entity;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, RwLock};
#[derive(Debug)]
pub struct MapStorage<T> {
map: HashMap<u32, Option<T>>,
entities: Arc<RwLock<Entities>>,
}
impl<T> MapStorage<T> {
pub fn new(entity_allocator: Arc<RwLock<Entities>>) -> Self {
Self {
map: HashMap::new(),
entities: entity_allocator,
}
}
pub fn get(&self, entity: Entity) -> Option<&T> {
let lock = self.entities.read().unwrap();
if lock.exists(entity) {
self.map.get(&entity.index).unwrap_or(&None).as_ref()
} else {
None
}
}
pub fn get_mut(&mut self, entity: Entity) -> Option<&mut T> {
let lock = self.entities.read().unwrap();
if lock.exists(entity) {
if let Some(entry) = self.map.get_mut(&entity.index) {
entry.as_mut()
} else {
None
}
} else {
None
}
}
pub fn set(&mut self, entity: Entity, data: T) -> Result<Option<T>, NoSuchEntity> {
let lock = self.entities.read().unwrap();
if lock.exists(entity) {
let entry = self.map.entry(entity.index);
Ok(entry.or_default().replace(data))
} else {
Err(NoSuchEntity {})
}
}
pub fn remove_unchecked(&mut self, entity: Entity) -> Option<T> {
if let Some(entry) = self.map.get_mut(&entity.index) {
entry.take()
} else {
None
}
}
pub fn remove(&mut self, entity: Entity) -> Result<Option<T>, NoSuchEntity> {
let lock = self.entities.read().unwrap();
if lock.exists(entity) {
if let Some(entry) = self.map.get_mut(&entity.index) {
Ok(entry.take())
} else {
Ok(None)
}
} else {
Err(NoSuchEntity)
}
}
pub fn clear(&mut self) {
self.map.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
struct MapTestData(i32);
#[test]
fn map_get_not_set() {
let entities = Arc::new(RwLock::new(Entities::new(3)));
let map = MapStorage::<MapTestData>::new(Arc::clone(&entities));
let entity = Entity {
index: 0,
generation: 0,
};
let entry = map.get(entity);
assert_eq!(entry, None);
}
#[test]
fn map_get() -> Result<(), NoSuchEntity> {
let entities = Arc::new(RwLock::new(Entities::new(3)));
let mut map = MapStorage::<MapTestData>::new(Arc::clone(&entities));
let entity = {
let mut lock = entities.write().unwrap();
lock.spawn()
};
let data = MapTestData(42);
let old_data = map.set(entity, data)?;
assert_eq!(old_data, None);
let entry = map.get(entity);
assert_eq!(entry, Some(&data));
Ok(())
}
#[test]
fn map_set_exists() -> Result<(), NoSuchEntity> {
let entities = Arc::new(RwLock::new(Entities::new(3)));
let mut map = MapStorage::<MapTestData>::new(Arc::clone(&entities));
let entity = {
let mut lock = entities.write().unwrap();
lock.spawn()
};
let data = MapTestData(42);
let old_data = map.set(entity, data)?;
assert_eq!(old_data, None);
let entity = Entity {
index: 0,
generation: 1,
};
let no_such_entity = map.set(entity, data);
assert!(no_such_entity.is_err());
Ok(())
}
#[test]
fn remove_missing_is_ok() -> Result<(), NoSuchEntity> {
let entities = Arc::new(RwLock::new(Entities::new(3)));
let mut map = MapStorage::<MapTestData>::new(Arc::clone(&entities));
let entity = {
let mut lock = entities.write().unwrap();
lock.spawn()
};
let no_data = map.remove(entity)?;
assert_eq!(no_data, None);
Ok(())
}
#[test]
fn can_insert_after_remove() -> Result<(), NoSuchEntity> {
let entities = Arc::new(RwLock::new(Entities::new(3)));
let mut map = MapStorage::<MapTestData>::new(Arc::clone(&entities));
let entity = {
let mut lock = entities.write().unwrap();
lock.spawn()
};
let data = MapTestData(42);
let old_data = map.set(entity, data)?;
assert_eq!(old_data, None);
let old_data = map.remove(entity)?;
assert_eq!(old_data, Some(MapTestData(42)));
let missing_entry = map.get(entity);
assert_eq!(missing_entry, None);
let entity = {
let mut lock = entities.write().unwrap();
lock.despawn(entity)?;
lock.spawn()
};
let data = MapTestData(17);
let old_data = map.set(entity, data)?;
assert_eq!(old_data, None);
let entry = map.get(entity);
assert_eq!(entry, Some(&data));
Ok(())
}
#[test]
fn map_iter() -> Result<(), NoSuchEntity> {
let entities = Arc::new(RwLock::new(Entities::new(3)));
let mut map = MapStorage::<MapTestData>::new(Arc::clone(&entities));
let entity_a = {
let mut lock = entities.write().unwrap();
lock.spawn()
};
let old_data = map.set(entity_a, MapTestData(17))?;
assert_eq!(old_data, None);
let entity_b = {
let mut lock = entities.write().unwrap();
lock.spawn()
};
let old_data = map.set(entity_b, MapTestData(42))?;
assert_eq!(old_data, None);
let entity_c = {
let mut lock = entities.write().unwrap();
lock.spawn()
};
let old_data = map.set(entity_c, MapTestData(123))?;
assert_eq!(old_data, None);
let old_data = map.remove(entity_c)?; assert_eq!(old_data, Some(MapTestData(123)));
let mut v = entities
.read()
.unwrap()
.iter()
.map(|entity| (entity, map.get(entity)))
.filter(|(_entity, data)| data.is_some())
.collect::<Vec<_>>();
v.sort_by(|(entity_a, _a), (entity_b, _b)| entity_a.index.cmp(&entity_b.index));
assert_eq!(
v,
vec![
(entity_a, Some(&MapTestData(17))),
(entity_b, Some(&MapTestData(42)))
]
);
Ok(())
}
}