use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::collections::hash_map::{
Entry as HashMapEntry,
OccupiedEntry as HashMapOccupiedEntry,
VacantEntry as HashMapVacantEntry,
};
use std::marker::PhantomData;
pub trait TypeMapKey: Any {
type Value: Send + Sync;
}
pub struct TypeMap(HashMap<TypeId, Box<(dyn Any + Send + Sync)>>);
impl TypeMap {
#[inline]
pub fn new() -> Self {
Self(HashMap::new())
}
#[inline]
pub fn insert<T>(&mut self, value: T::Value)
where
T: TypeMapKey
{
self.0.insert(TypeId::of::<T>(), Box::new(value));
}
#[inline]
pub fn entry<T>(&mut self) -> Entry<'_, T>
where
T: TypeMapKey
{
match self.0.entry(TypeId::of::<T>()) {
HashMapEntry::Occupied(entry) => Entry::Occupied(OccupiedEntry {
entry,
_marker: PhantomData,
}),
HashMapEntry::Vacant(entry) => Entry::Vacant(VacantEntry {
entry,
_marker: PhantomData,
})
}
}
#[inline]
pub fn get<T>(&self) -> Option<&T::Value>
where
T: TypeMapKey
{
self.0
.get(&TypeId::of::<T>())
.and_then(|b| b.downcast_ref::<T::Value>())
}
#[inline]
pub fn get_mut<T>(&mut self) -> Option<&mut T::Value>
where
T: TypeMapKey
{
self.0
.get_mut(&TypeId::of::<T>())
.and_then(|b| b.downcast_mut::<T::Value>())
}
}
pub enum Entry<'a, K>
where
K: TypeMapKey,
{
Occupied(OccupiedEntry<'a, K>),
Vacant(VacantEntry<'a, K>),
}
impl<'a, K> Entry<'a, K>
where
K: TypeMapKey,
{
#[inline]
pub fn or_insert(self, value: K::Value) -> &'a mut K::Value {
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(value),
}
}
#[inline]
pub fn or_insert_with<F>(self, f: F) -> &'a mut K::Value
where
F: FnOnce() -> K::Value
{
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(f()),
}
}
#[inline]
pub fn and_modify<F>(self, f: F) -> Self
where
F: FnOnce(&mut K::Value)
{
match self {
Entry::Occupied(mut entry) => {
f(entry.get_mut());
Entry::Occupied(entry)
},
Entry::Vacant(entry) => Entry::Vacant(entry),
}
}
}
impl<'a, K> Entry<'a, K>
where
K: TypeMapKey,
K::Value: Default
{
#[inline]
pub fn or_default(self) -> &'a mut K::Value {
self.or_insert_with(<K::Value as Default>::default)
}
}
pub struct OccupiedEntry<'a, K>
where
K: TypeMapKey,
{
entry: HashMapOccupiedEntry<'a, TypeId, Box<(dyn Any + Send + Sync)>>,
_marker: PhantomData<&'a K::Value>,
}
impl<'a, K> OccupiedEntry<'a, K>
where
K: TypeMapKey,
{
#[inline]
pub fn get(&self) -> &K::Value {
self.entry.get().downcast_ref().unwrap()
}
#[inline]
pub fn get_mut(&mut self) -> &mut K::Value {
self.entry.get_mut().downcast_mut().unwrap()
}
#[inline]
pub fn into_mut(self) -> &'a mut K::Value {
self.entry.into_mut().downcast_mut().unwrap()
}
#[inline]
pub fn insert(&mut self, value: K::Value) {
self.entry.insert(Box::new(value));
}
#[inline]
pub fn remove(self) {
self.entry.remove();
}
}
pub struct VacantEntry<'a, K>
where
K: TypeMapKey,
{
entry: HashMapVacantEntry<'a, TypeId, Box<(dyn Any + Send + Sync)>>,
_marker: PhantomData<&'a K::Value>,
}
impl<'a, K> VacantEntry<'a, K>
where
K: TypeMapKey,
{
#[inline]
pub fn insert(self, value: K::Value) -> &'a mut K::Value {
self.entry.insert(Box::new(value)).downcast_mut().unwrap()
}
}
#[cfg(test)]
mod test {
use super::*;
struct Counter;
impl TypeMapKey for Counter {
type Value = u64;
}
#[test]
fn typemap_counter() {
let mut map = TypeMap::new();
map.insert::<Counter>(0);
assert_eq!(*map.get::<Counter>().unwrap(), 0);
for _ in 0..100 {
*map.get_mut::<Counter>().unwrap() += 1;
}
assert_eq!(*map.get::<Counter>().unwrap(), 100);
}
#[test]
fn typemap_entry() {
let mut map = TypeMap::new();
assert_eq!(map.get::<Counter>(), None);
*map.entry::<Counter>().or_insert(0) += 42;
assert_eq!(*map.get::<Counter>().unwrap(), 42);
}
}