use std::{
any::{Any, TypeId},
collections::HashMap,
marker::PhantomData,
};
use thunderdome::{Arena, Index};
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Id<T>(Index, std::marker::PhantomData<T>);
impl<T> Copy for Id<T> {}
impl<T> Clone for Id<T> {
fn clone(&self) -> Self {
*self
}
}
#[derive(Default)]
pub struct Store {
pub data: HashMap<TypeId, Box<dyn Any>>,
}
impl Store {
pub fn new() -> Self {
Self::default()
}
pub fn spawn<T: 'static>(&mut self, value: T) -> Id<T> {
let type_id = TypeId::of::<T>();
let idx = if let Some(arena) = self.data.get_mut(&type_id) {
arena.downcast_mut::<Arena<T>>().unwrap().insert(value)
} else {
let mut arena = Arena::new();
let idx = arena.insert(value);
self.data.insert(type_id, Box::new(arena));
idx
};
Id(idx, PhantomData)
}
pub fn get<T: 'static>(&self, id: Id<T>) -> Option<&T> {
let type_id = TypeId::of::<T>();
if let Some(arena) = self.data.get(&type_id) {
arena.downcast_ref::<Arena<T>>().unwrap().get(id.0)
} else {
None
}
}
pub fn get_mut<T: 'static>(&mut self, id: Id<T>) -> Option<&mut T> {
let type_id = TypeId::of::<T>();
if let Some(arena) = self.data.get_mut(&type_id) {
arena.downcast_mut::<Arena<T>>().unwrap().get_mut(id.0)
} else {
None
}
}
pub fn iter<T: 'static>(&self) -> Box<dyn Iterator<Item = (Id<T>, &T)> + '_> {
let type_id = TypeId::of::<T>();
if let Some(arena) = self.data.get(&type_id) {
Box::new(
arena
.downcast_ref::<Arena<T>>()
.unwrap()
.iter()
.map(|x| (Id(x.0, PhantomData), x.1)),
)
} else {
Box::new(std::iter::empty())
}
}
pub fn iter_mut<T: 'static>(&mut self) -> Box<dyn Iterator<Item = (Id<T>, &mut T)> + '_> {
let type_id = TypeId::of::<T>();
if let Some(arena) = self.data.get_mut(&type_id) {
Box::new(
arena
.downcast_mut::<Arena<T>>()
.unwrap()
.iter_mut()
.map(|x| (Id(x.0, PhantomData), x.1)),
)
} else {
Box::new(std::iter::empty())
}
}
pub fn remove<T: 'static>(&mut self, id: Id<T>) -> Option<T> {
let type_id = TypeId::of::<T>();
if let Some(arena) = self.data.get_mut(&type_id) {
arena.downcast_mut::<Arena<T>>().unwrap().remove(id.0)
} else {
None
}
}
pub fn get2_mut<T: 'static>(
&mut self,
id1: Id<T>,
id2: Id<T>,
) -> (Option<&mut T>, Option<&mut T>) {
let type_id = TypeId::of::<T>();
if let Some(arena) = self.data.get_mut(&type_id) {
arena
.downcast_mut::<Arena<T>>()
.unwrap()
.get2_mut(id1.0, id2.0)
} else {
(None, None)
}
}
}
impl<T: 'static> std::ops::Index<Id<T>> for Store {
type Output = T;
fn index(&self, index: Id<T>) -> &Self::Output {
self.get(index).unwrap()
}
}
impl<T: 'static> std::ops::IndexMut<Id<T>> for Store {
fn index_mut(&mut self, index: Id<T>) -> &mut T {
self.get_mut(index).unwrap()
}
}
#[cfg(test)]
mod tests {
use crate::Store;
#[test]
fn empty_store_is_okay() {
let store = Store::new();
assert_eq!(None, store.iter::<i32>().next());
assert_eq!(None, store.iter::<f32>().next());
}
#[test]
fn simple_test() {
let mut store = Store::new();
struct Enemy {
pub x: i32,
}
struct Player {
pub health: f32,
}
store.spawn(Enemy { x: 1 });
store.spawn(Enemy { x: 2 });
let player = store.spawn(Player { health: 100.0 });
assert_eq!(
&[1, 2],
store
.iter::<Enemy>()
.map(|t| t.1.x)
.collect::<Vec<_>>()
.as_slice()
);
assert_eq!(100.0, store.get(player.clone()).unwrap().health);
store.get_mut(player).unwrap().health = 200.0;
assert_eq!(200.0, store.get(player).unwrap().health);
}
#[test]
fn index_test() {
let mut store = Store::new();
let id = store.spawn(3);
assert_eq!(3, store[id]);
assert_eq!(&3, store.get(id).unwrap());
}
#[test]
fn index_mut_test() {
let mut store = Store::new();
let id = store.spawn(3);
assert_eq!(3, store[id]);
store[id] = 4;
assert_eq!(4, store[id]);
}
#[test]
#[should_panic(expected = "is called with two identical indices")]
fn get2_mut_panics_test() {
let mut store = Store::new();
let id = store.spawn(3);
store.get2_mut(id, id);
}
}