pub use self::{
data::{Read, ReadExpect, Write, WriteExpect},
entry::Entry,
setup::{DefaultProvider, PanicHandler, SetupHandler},
};
use std::{
any::{Any, TypeId},
marker::PhantomData,
ops::{Deref, DerefMut},
};
use ahash::AHashMap as HashMap;
use crate::{
cell::{Ref, RefMut, TrustCell},
SystemData,
};
use self::entry::create_entry;
mod data;
mod entry;
mod res_downcast;
#[macro_use]
mod setup;
pub struct Fetch<'a, T: 'a> {
inner: Ref<'a, dyn Resource>,
phantom: PhantomData<&'a T>,
}
impl<'a, T> Deref for Fetch<'a, T>
where
T: Resource,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { self.inner.downcast_ref_unchecked() }
}
}
impl<'a, T> Clone for Fetch<'a, T> {
fn clone(&self) -> Self {
Fetch {
inner: self.inner.clone(),
phantom: PhantomData,
}
}
}
pub struct FetchMut<'a, T: 'a> {
inner: RefMut<'a, dyn Resource>,
phantom: PhantomData<&'a mut T>,
}
impl<'a, T> Deref for FetchMut<'a, T>
where
T: Resource,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { self.inner.downcast_ref_unchecked() }
}
}
impl<'a, T> DerefMut for FetchMut<'a, T>
where
T: Resource,
{
fn deref_mut(&mut self) -> &mut T {
unsafe { self.inner.downcast_mut_unchecked() }
}
}
#[cfg(feature = "parallel")]
pub trait Resource: Any + Send + Sync + 'static {}
#[cfg(not(feature = "parallel"))]
pub trait Resource: Any + 'static {}
#[cfg(feature = "parallel")]
impl<T> Resource for T where T: Any + Send + Sync {}
#[cfg(not(feature = "parallel"))]
impl<T> Resource for T where T: Any {}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ResourceId {
type_id: TypeId,
dynamic_id: u64,
}
impl ResourceId {
#[inline]
pub fn new<T: Resource>() -> Self {
ResourceId::new_with_dynamic_id::<T>(0)
}
#[inline]
pub fn from_type_id(type_id: TypeId) -> Self {
ResourceId::from_type_id_and_dynamic_id(type_id, 0)
}
#[inline]
pub fn new_with_dynamic_id<T: Resource>(dynamic_id: u64) -> Self {
ResourceId::from_type_id_and_dynamic_id(TypeId::of::<T>(), dynamic_id)
}
#[inline]
pub fn from_type_id_and_dynamic_id(type_id: TypeId, dynamic_id: u64) -> Self {
ResourceId {
type_id,
dynamic_id,
}
}
fn assert_same_type_id<R: Resource>(&self) {
let res_id0 = ResourceId::new::<R>();
assert_eq!(
res_id0.type_id, self.type_id,
"Passed a `ResourceId` with a wrong type ID"
);
}
}
#[derive(Default)]
pub struct World {
resources: HashMap<ResourceId, TrustCell<Box<dyn Resource>>>,
}
impl World {
pub fn empty() -> Self {
Default::default()
}
pub fn insert<R>(&mut self, r: R)
where
R: Resource,
{
self.insert_by_id(ResourceId::new::<R>(), r);
}
pub fn remove<R>(&mut self) -> Option<R>
where
R: Resource,
{
self.remove_by_id(ResourceId::new::<R>())
}
pub fn has_value<R>(&self) -> bool
where
R: Resource,
{
self.has_value_raw(ResourceId::new::<R>())
}
pub fn has_value_raw(&self, id: ResourceId) -> bool {
self.resources.contains_key(&id)
}
pub fn entry<R>(&mut self) -> Entry<R>
where
R: Resource,
{
create_entry(self.resources.entry(ResourceId::new::<R>()))
}
pub fn system_data<'a, T>(&'a self) -> T
where
T: SystemData<'a>,
{
SystemData::fetch(self)
}
pub fn setup<'a, T: SystemData<'a>>(&mut self) {
T::setup(self);
}
pub fn exec<'a, F, R, T>(&'a mut self, f: F) -> R
where
F: FnOnce(T) -> R,
T: SystemData<'a>,
{
self.setup::<T>();
f(self.system_data())
}
pub fn fetch<T>(&self) -> Fetch<T>
where
T: Resource,
{
self.try_fetch().unwrap_or_else(|| {
if self.resources.is_empty() {
eprintln!(
"Note: Could not find a resource (see the following panic);\
the `World` is completely empty. Did you accidentally create a fresh `World`?"
)
}
fetch_panic!()
})
}
pub fn try_fetch<T>(&self) -> Option<Fetch<T>>
where
T: Resource,
{
let res_id = ResourceId::new::<T>();
self.resources.get(&res_id).map(|r| Fetch {
inner: Ref::map(r.borrow(), Box::as_ref),
phantom: PhantomData,
})
}
pub fn try_fetch_by_id<T>(&self, id: ResourceId) -> Option<Fetch<T>>
where
T: Resource,
{
id.assert_same_type_id::<T>();
self.resources.get(&id).map(|r| Fetch {
inner: Ref::map(r.borrow(), Box::as_ref),
phantom: PhantomData,
})
}
pub fn fetch_mut<T>(&self) -> FetchMut<T>
where
T: Resource,
{
self.try_fetch_mut().unwrap_or_else(|| fetch_panic!())
}
pub fn try_fetch_mut<T>(&self) -> Option<FetchMut<T>>
where
T: Resource,
{
let res_id = ResourceId::new::<T>();
self.resources.get(&res_id).map(|r| FetchMut {
inner: RefMut::map(r.borrow_mut(), Box::as_mut),
phantom: PhantomData,
})
}
pub fn try_fetch_mut_by_id<T>(&self, id: ResourceId) -> Option<FetchMut<T>>
where
T: Resource,
{
id.assert_same_type_id::<T>();
self.resources.get(&id).map(|r| FetchMut {
inner: RefMut::map(r.borrow_mut(), Box::as_mut),
phantom: PhantomData,
})
}
pub fn insert_by_id<R>(&mut self, id: ResourceId, r: R)
where
R: Resource,
{
id.assert_same_type_id::<R>();
self.resources.insert(id, TrustCell::new(Box::new(r)));
}
pub fn remove_by_id<R>(&mut self, id: ResourceId) -> Option<R>
where
R: Resource,
{
#![allow(clippy::redundant_closure)]
id.assert_same_type_id::<R>();
self.resources
.remove(&id)
.map(TrustCell::into_inner)
.map(|x: Box<dyn Resource>| x.downcast())
.map(|x: Result<Box<R>, _>| x.ok().unwrap())
.map(|x| *x)
}
pub fn try_fetch_internal(&self, id: ResourceId) -> Option<&TrustCell<Box<dyn Resource>>> {
self.resources.get(&id)
}
pub fn get_mut<T: Resource>(&mut self) -> Option<&mut T> {
self.get_mut_raw(ResourceId::new::<T>())
.map(|res| unsafe { res.downcast_mut_unchecked() })
}
pub fn get_mut_raw(&mut self, id: ResourceId) -> Option<&mut dyn Resource> {
self.resources
.get_mut(&id)
.map(TrustCell::get_mut)
.map(Box::as_mut)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{RunNow, System, SystemData};
#[derive(Default)]
struct Res;
#[test]
fn fetch_aspects() {
assert_eq!(Read::<Res>::reads(), vec![ResourceId::new::<Res>()]);
assert_eq!(Read::<Res>::writes(), vec![]);
let mut world = World::empty();
world.insert(Res);
<Read<Res> as SystemData>::fetch(&world);
}
#[test]
fn fetch_mut_aspects() {
assert_eq!(Write::<Res>::reads(), vec![]);
assert_eq!(Write::<Res>::writes(), vec![ResourceId::new::<Res>()]);
let mut world = World::empty();
world.insert(Res);
<Write<Res> as SystemData>::fetch(&world);
}
#[test]
fn fetch_by_id() {
#![allow(clippy::map_clone)]
let mut world = World::empty();
world.insert_by_id(ResourceId::new_with_dynamic_id::<i32>(1), 5);
world.insert_by_id(ResourceId::new_with_dynamic_id::<i32>(2), 15);
world.insert_by_id(ResourceId::new_with_dynamic_id::<i32>(3), 45);
assert_eq!(
world
.try_fetch_by_id::<i32>(ResourceId::new_with_dynamic_id::<i32>(2))
.map(|x| *x),
Some(15)
);
assert_eq!(
world
.try_fetch_by_id::<i32>(ResourceId::new_with_dynamic_id::<i32>(1))
.map(|x| *x),
Some(5)
);
assert_eq!(
world
.try_fetch_by_id::<i32>(ResourceId::new_with_dynamic_id::<i32>(3))
.map(|x| *x),
Some(45)
);
}
#[test]
fn system_data() {
let mut world = World::empty();
world.insert(5u32);
let x = *world.system_data::<Read<u32>>();
assert_eq!(x, 5);
}
#[test]
fn setup() {
let mut world = World::empty();
world.insert(5u32);
world.setup::<Read<u32>>();
let x = *world.system_data::<Read<u32>>();
assert_eq!(x, 5);
world.remove::<u32>();
world.setup::<Read<u32>>();
let x = *world.system_data::<Read<u32>>();
assert_eq!(x, 0);
}
#[test]
fn exec() {
#![allow(clippy::float_cmp)]
let mut world = World::empty();
world.exec(|(float, boolean): (Read<f32>, Read<bool>)| {
assert_eq!(*float, 0.0);
assert!(!*boolean);
});
world.exec(|(mut float, mut boolean): (Write<f32>, Write<bool>)| {
*float = 4.3;
*boolean = true;
});
world.exec(|(float, boolean): (Read<f32>, ReadExpect<bool>)| {
assert_eq!(*float, 4.3);
assert!(*boolean);
});
}
#[test]
#[should_panic]
fn exec_panic() {
let mut world = World::empty();
world.exec(|(_float, _boolean): (Write<f32>, Write<bool>)| {
panic!();
});
}
#[test]
#[should_panic]
fn invalid_fetch_by_id0() {
let mut world = World::empty();
world.insert(5i32);
world.try_fetch_by_id::<u32>(ResourceId::new_with_dynamic_id::<i32>(111));
}
#[test]
#[should_panic]
fn invalid_fetch_by_id1() {
let mut world = World::empty();
world.insert(5i32);
world.try_fetch_by_id::<i32>(ResourceId::new_with_dynamic_id::<u32>(111));
}
#[test]
fn add() {
struct Foo;
let mut world = World::empty();
world.insert(Res);
assert!(world.has_value::<Res>());
assert!(!world.has_value::<Foo>());
}
#[allow(unused)]
#[test]
#[should_panic(expected = "but it was already borrowed")]
fn read_write_fails() {
let mut world = World::empty();
world.insert(Res);
let read: Fetch<Res> = world.fetch();
let write: FetchMut<Res> = world.fetch_mut();
}
#[allow(unused)]
#[test]
#[should_panic(expected = "but it was already borrowed mutably")]
fn write_read_fails() {
let mut world = World::empty();
world.insert(Res);
let write: FetchMut<Res> = world.fetch_mut();
let read: Fetch<Res> = world.fetch();
}
#[test]
fn remove_insert() {
let mut world = World::empty();
world.insert(Res);
assert!(world.has_value::<Res>());
println!("{:#?}", world.resources.keys().collect::<Vec<_>>());
world.remove::<Res>().unwrap();
assert!(!world.has_value::<Res>());
world.insert(Res);
assert!(world.has_value::<Res>());
}
#[test]
fn default_works() {
struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = Write<'a, i32>;
fn run(&mut self, mut data: Self::SystemData) {
assert_eq!(*data, 0);
*data = 33;
}
}
let mut world = World::empty();
assert!(world.try_fetch::<i32>().is_none());
let mut sys = Sys;
RunNow::setup(&mut sys, &mut world);
sys.run_now(&world);
assert!(world.try_fetch::<i32>().is_some());
assert_eq!(*world.fetch::<i32>(), 33);
}
}