use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use std::hash::{BuildHasherDefault, Hasher};
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
#[derive(Default)]
struct IdHasher(u64);
impl Hasher for IdHasher {
#[inline]
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, _: &[u8]) {
unreachable!("TypeId calls write_u64");
}
#[inline]
fn write_u64(&mut self, id: u64) {
self.0 = id;
}
}
#[derive(Default)]
pub struct PropertyBag {
map: AnyMap,
}
impl PropertyBag {
#[inline]
pub fn new() -> PropertyBag {
PropertyBag {
map: AnyMap::default(),
}
}
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(|boxed| {
(boxed as Box<dyn Any + 'static>)
.downcast()
.ok()
.map(|boxed| *boxed)
})
}
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())
.and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref())
}
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
self.map
.get_mut(&TypeId::of::<T>())
.and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut())
}
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.map.remove(&TypeId::of::<T>()).and_then(|boxed| {
(boxed as Box<dyn Any + 'static>)
.downcast()
.ok()
.map(|boxed| *boxed)
})
}
#[inline]
pub fn clear(&mut self) {
self.map.clear();
}
}
impl fmt::Debug for PropertyBag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PropertyBag").finish()
}
}
#[derive(Clone, Debug, Default)]
pub struct SharedPropertyBag(Arc<Mutex<PropertyBag>>);
impl SharedPropertyBag {
pub fn new() -> Self {
SharedPropertyBag(Arc::new(Mutex::new(PropertyBag::new())))
}
pub fn acquire(&self) -> impl Deref<Target = PropertyBag> + '_ {
self.0.lock().unwrap()
}
pub fn acquire_mut(&self) -> impl DerefMut<Target = PropertyBag> + '_ {
self.0.lock().unwrap()
}
}
impl From<PropertyBag> for SharedPropertyBag {
fn from(bag: PropertyBag) -> Self {
SharedPropertyBag(Arc::new(Mutex::new(bag)))
}
}
#[cfg(test)]
#[test]
fn test_extensions() {
#[derive(Debug, PartialEq)]
struct MyType(i32);
let mut extensions = PropertyBag::new();
extensions.insert(5i32);
extensions.insert(MyType(10));
assert_eq!(extensions.get(), Some(&5i32));
assert_eq!(extensions.get_mut(), Some(&mut 5i32));
assert_eq!(extensions.remove::<i32>(), Some(5i32));
assert!(extensions.get::<i32>().is_none());
assert_eq!(extensions.get::<bool>(), None);
assert_eq!(extensions.get(), Some(&MyType(10)));
}