use crate::prelude_lib::*;
use std::collections::hash_map::Entry as MapEntry;
use std::collections::HashMap;
use std::sync::RwLock;
use std::any::Any;
#[derive(Default)]
pub struct Universe {
pub(crate) objects: RwLock<HashMap<TypeId, Box<Locked>>>,
}
unsafe impl Send for Universe {}
unsafe impl Sync for Universe {}
impl Universe {
pub fn new() -> Self {
Self::default()
}
fn insert(map: &mut HashMap<TypeId, Box<Locked>>, ty: TypeId, obj: Box<Locked>) {
match map.entry(ty) {
MapEntry::Occupied(_) => {
panic!("object inserted twice")
},
MapEntry::Vacant(e) => e.insert(obj),
};
}
pub fn add<T: Any>(&self, key: TypeId, obj: T) {
let map = &mut *self.objects.write().unwrap();
Universe::insert(map, key, Locked::new(Box::new(obj), std::any::type_name::<T>()));
}
pub fn add_mut<T: Any>(&mut self, key: TypeId, obj: T) {
let map = &mut *self.objects.get_mut().unwrap();
let obj = Locked::new(Box::new(obj), std::any::type_name::<T>());
Universe::insert(map, key, obj);
}
pub fn remove<T: Any>(&self, key: TypeId) -> Option<Box<dyn Any>> {
self.objects
.write()
.unwrap()
.remove(&key)
.map(|l| l.into_inner())
}
pub fn remove_mut<T: Any>(&mut self, key: TypeId) -> Option<Box<dyn Any>> {
self.objects
.get_mut()
.unwrap()
.remove(&key)
.map(|l| l.into_inner())
}
pub fn has<T: Any>(&self) -> bool {
self.objects
.read()
.unwrap()
.get(&TypeId::of::<T>())
.is_some()
}
}
impl Universe {
pub fn all_mut(&mut self, mut each: impl FnMut(&mut dyn Any)) {
let mut objs = self.objects.write().unwrap();
for lock in objs.values_mut() {
unsafe {
let mut lock = lock.write();
let obj: &mut dyn Any = &mut *lock;
each(obj);
}
}
}
pub fn all_ref(&self, mut each: impl FnMut(&dyn Any)) {
let mut objs = self.objects.write().unwrap();
for lock in objs.values_mut() {
unsafe {
let lock = lock.read();
let obj: &dyn Any = &*lock;
each(obj);
}
}
}
}
impl Universe {
pub fn clone_value<T: Any + Clone>(&self) -> T {
self.with(T::clone)
}
pub fn with<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> R {
self.with_obj(TypeId::of::<T>(), |obj| {
let obj = obj.downcast_ref().expect("type mismatch");
f(obj)
})
}
pub fn with_mut<T: Any, R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
self.with_obj_mut(TypeId::of::<T>(), |obj| {
let obj = obj.downcast_mut().expect("type mismatch");
f(obj)
})
}
pub fn with_obj<R>(&self, ty: TypeId, f: impl FnOnce(&dyn Any) -> R) -> R {
self.with_access(ty, Access::Read, move |obj| unsafe {
let obj = &*obj;
f(obj)
})
}
pub fn with_obj_mut<R>(&self, ty: TypeId, f: impl FnOnce(&mut dyn Any) -> R) -> R {
self.with_access(ty, Access::Write, move |obj| unsafe {
let obj = &mut *obj;
f(obj)
})
}
pub fn with_access<R>(
&self,
ty: TypeId,
access: Access,
f: impl FnOnce(*mut dyn Any) -> R,
) -> R {
loop {
let mut objects = self.objects.write().unwrap();
let obj = objects.get_mut(&ty).expect("type not found");
if obj.can(access) {
obj.acquire(access);
let obj = unsafe { obj.contents() };
mem::drop(objects);
let ret = f(obj);
let mut objects = self.objects.write().unwrap();
let obj = objects.get_mut(&ty).expect("type lost");
obj.release(access);
return ret;
}
}
}
pub fn lock_state_dump(&self) {
let objects = self.objects.read().unwrap();
for (ty, val) in objects.iter() {
println!(" {:?}\t{:?}", ty, val.state);
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::fmt::Write;
unsafe impl<'a> Extract for &'a mut String {
fn each_resource(f: &mut dyn FnMut(TypeId, Access)) {
f(TypeId::of::<String>(), Access::Write);
}
type Owned = Self;
unsafe fn extract(_universe: &Universe, rez: &mut Rez) -> Self {
rez.take_mut_downcast()
}
unsafe fn convert(_universe: &Universe, owned: *mut Self::Owned) -> Self {
*(&mut *owned)
}
type Cleanup = ();
}
#[test]
fn construction() {
let _universe = Universe::new();
}
#[test]
fn single_string() {
let mut universe = Universe::new();
let key = TypeId::of::<String>();
universe.add_mut(key, format!("Hello"));
}
#[test]
#[should_panic]
fn conflicting_strings() {
let mut universe = Universe::new();
let key = TypeId::of::<String>();
universe.add_mut(key, format!("oh no"));
universe.add_mut(key, format!("o noez"));
}
#[test]
fn look_string() {
let mut universe = Universe::new();
universe.add_mut(TypeId::of::<String>(), format!("Hello"));
universe.kmap(|text: &mut String| {
println!("Hey!");
println!("We've got: {:?}", text);
});
}
#[test]
#[should_panic]
fn look_for_missing_string() {
let universe = Universe::new();
universe.kmap(|_: &mut String| {});
}
#[test]
fn change_string() {
let mut universe = Universe::new();
universe.add_mut(TypeId::of::<String>(), format!("Hello"));
universe.kmap(|text: &mut String| {
println!("We've got: {:?}", text);
write!(text, " World").ok();
});
universe.kmap(|text: &mut String| {
assert_eq!(text, "Hello World");
});
}
#[test]
fn universe_claims_to_be_threadsafe() {
fn assert<T: Send + Sync>() {}
assert::<Universe>();
}
}
#[macro_export]
macro_rules! decl_context {
(
$(#[$meta:meta])*
$vis:vis struct $name:ident {
$(
$(#[$cmeta:meta])*
$cvis:vis $cn:ident
$(: &mut $cty_mut:ty,)?
$(: &$cty_ref:ty,)?
$(: $cty_path:path,)?
)*
}
) => {
$crate::paste::item! {
#[allow(unused_imports)]
$vis use self::[<_v9_impl_ $name>]::$name;
#[allow(non_snake_case)]
mod [<_v9_impl_ $name>] {
#[allow(non_camel_case_types)]
mod path {
use super::super::*;
$(
pub type [<_v9_ctx_ $name _ $cn>]<'a> =
$(&'a mut $cty_mut)?
$(&'a $cty_ref)?
$($cty_path<'a>)?
;
)*
}
#[allow(non_camel_case_types)]
mod cn {
$(pub type $cn<'a> = super::path::[<_v9_ctx_ $name _ $cn>]<'a>;)*
}
#[allow(non_camel_case_types)]
mod owned {
$(pub type $cn = <super::cn::$cn<'static> as super::Extract>::Owned;)*
}
use $crate::prelude_macro::*;
$(#[$meta])*
pub struct $name<'a> {
$(
$(#[$cmeta])*
$cvis $cn: self::cn::$cn<'a>,
)*
}
pub struct __OwnedContext {
$($cn: self::owned::$cn,)*
}
unsafe impl<'a> Extract for $name<'a> {
fn each_resource(f: &mut dyn FnMut(TypeId, Access)) {
$(<self::cn::$cn<'static> as Extract>::each_resource(f);)*
}
type Owned = __OwnedContext;
unsafe fn extract(universe: &Universe, rez: &mut Rez) -> Self::Owned {
__OwnedContext {
$($cn: <self::cn::$cn<'static> as Extract>::extract(universe, rez),)*
}
}
unsafe fn convert(universe: &Universe, owned: *mut Self::Owned) -> Self {
let owned: &mut __OwnedContext = &mut *owned;
$name {
$($cn: <self::cn::$cn<'static> as Extract>::convert(universe, &mut owned.$cn),)*
}
}
type Cleanup = __OwnedCleanup;
}
pub struct __OwnedCleanup {
$($cn: <self::cn::$cn<'static> as Extract>::Cleanup,)*
}
unsafe impl<'a> Cleaner<$name<'a>> for __OwnedCleanup {
fn pre_cleanup(owned: __OwnedContext, universe: &Universe) -> Self {
Self {
$($cn: {
type T = self::cn::$cn<'static>;
<<T as Extract>::Cleanup as Cleaner<T>>::pre_cleanup(owned.$cn, universe)
},)*
}
}
fn post_cleanup(self, universe: &Universe) {
$(Cleaner::<self::cn::$cn<'static>>::post_cleanup(self.$cn, universe);)*
}
}
}
}
};
}
pub trait Register {
fn register(universe: &mut Universe);
}
#[repr(transparent)]
pub struct UniverseRef<'a> {
universe: &'a Universe,
}
unsafe impl<'a> Send for UniverseRef<'a> {}
unsafe impl<'a> Sync for UniverseRef<'a> {}
impl<'a> Deref for UniverseRef<'a> {
type Target = Universe;
fn deref(&self) -> &Universe { self.universe }
}
unsafe impl<'a> Extract for UniverseRef<'a> {
fn each_resource(_f: &mut dyn FnMut(TypeId, Access)) {}
type Owned = ();
unsafe fn extract(_universe: &Universe, _rez: &mut Rez) -> Self::Owned {}
unsafe fn convert(universe: &Universe, _owned: *mut Self::Owned) -> Self {
UniverseRef {
universe: {
&*(universe as *const _)
},
}
}
type Cleanup = ();
}
#[cfg(doctest)]
struct SoundnessChecks;