Source code
Revision control
Copy as Markdown
Other Tools
use std::any::{Any, TypeId};
use rustc_hash::FxHashMap;
use std::collections::hash_map;
use std::marker::PhantomData;
/// Prepared key-value pair
pub struct KvPair(TypeId, Box<dyn Any>);
impl KvPair {
pub fn new<T: 'static>(value: T) -> Self {
KvPair(TypeId::of::<T>(), Box::new(value))
}
pub fn extract<T: 'static>(self) -> Result<T, Self> {
let KvPair(key, value) = self;
value
.downcast()
.map(|boxed| *boxed)
.map_err(|e| KvPair(key, e))
}
}
/// A view into an occupied entry in a `TypeMap`.
#[derive(Debug)]
pub struct OccupiedEntry<'a, T> {
data: hash_map::OccupiedEntry<'a, TypeId, Box<dyn Any>>,
marker: PhantomData<fn(T)>,
}
impl<'a, T: 'static> OccupiedEntry<'a, T> {
/// Gets a reference to the value in the entry.
pub fn get(&self) -> &T {
self.data.get().downcast_ref().unwrap()
}
///Gets a mutable reference to the value in the entry.
pub fn get_mut(&mut self) -> &mut T {
self.data.get_mut().downcast_mut().unwrap()
}
/// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
/// with a lifetime bound to the map itself.
pub fn into_mut(self) -> &'a mut T {
self.data.into_mut().downcast_mut().unwrap()
}
/// Sets the value of the entry, and returns the entry's old value.
pub fn insert(&mut self, value: T) -> T {
self.data
.insert(Box::new(value))
.downcast()
.map(|boxed| *boxed)
.unwrap()
}
/// Takes the value out of the entry, and returns it.
pub fn remove(self) -> T {
self.data.remove().downcast().map(|boxed| *boxed).unwrap()
}
}
/// A view into a vacant entry in a `TypeMap`.
#[derive(Debug)]
pub struct VacantEntry<'a, T> {
data: hash_map::VacantEntry<'a, TypeId, Box<dyn Any>>,
marker: PhantomData<fn(T)>,
}
impl<'a, T: 'static> VacantEntry<'a, T> {
/// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it.
pub fn insert(self, value: T) -> &'a mut T {
self.data.insert(Box::new(value)).downcast_mut().unwrap()
}
}
/// A view into a single entry in a map, which may either be vacant or occupied.
#[derive(Debug)]
pub enum Entry<'a, T> {
Occupied(OccupiedEntry<'a, T>),
Vacant(VacantEntry<'a, T>),
}
impl<'a, T: 'static> Entry<'a, T> {
/// Ensures a value is in the entry by inserting the default if empty, and returns
/// a mutable reference to the value in the entry.
pub fn or_insert(self, default: T) -> &'a mut T {
match self {
Entry::Occupied(inner) => inner.into_mut(),
Entry::Vacant(inner) => inner.insert(default),
}
}
/// Ensures a value is in the entry by inserting the result of the default function if empty, and returns
/// a mutable reference to the value in the entry.
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T {
match self {
Entry::Occupied(inner) => inner.into_mut(),
Entry::Vacant(inner) => inner.insert(default()),
}
}
}
#[derive(Debug, Default)]
/// The typemap container
pub struct TypeMap {
map: Option<FxHashMap<TypeId, Box<dyn Any>>>,
}
impl TypeMap {
/// Create an empty `TypeMap`.
#[inline]
pub fn new() -> Self {
Self { map: None }
}
/// Insert a prepared `KvPair` into this `TypeMap`.
///
/// If a value of this type already exists, it will be returned.
pub fn insert_kv_pair(&mut self, KvPair(key, value): KvPair) -> Option<KvPair> {
self.map
.get_or_insert_with(FxHashMap::default)
.insert(key, value)
.map(|old_value| KvPair(key, old_value))
}
/// Insert a value into this `TypeMap`.
///
/// If a value of this type already exists, it will be returned.
pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> {
self.map
.get_or_insert_with(FxHashMap::default)
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
}
/// Check if container contains value for type
pub fn contains<T: 'static>(&self) -> bool {
self.map
.as_ref()
.and_then(|m| m.get(&TypeId::of::<T>()))
.is_some()
}
/// Get a reference to a value previously inserted on this `TypeMap`.
pub fn get<T: 'static>(&self) -> Option<&T> {
self.map
.as_ref()
.and_then(|m| m.get(&TypeId::of::<T>()))
.and_then(|boxed| boxed.downcast_ref())
}
/// Get a mutable reference to a value previously inserted on this `TypeMap`.
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.map
.as_mut()
.and_then(|m| m.get_mut(&TypeId::of::<T>()))
.and_then(|boxed| boxed.downcast_mut())
}
/// Remove a value from this `TypeMap`.
///
/// If a value of this type exists, it will be returned.
pub fn remove<T: 'static>(&mut self) -> Option<T> {
self.map
.as_mut()
.and_then(|m| m.remove(&TypeId::of::<T>()))
.and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
}
/// Clear the `TypeMap` of all inserted values.
#[inline]
pub fn clear(&mut self) {
self.map = None;
}
/// Get an entry in the `TypeMap` for in-place manipulation.
pub fn entry<T: 'static>(&mut self) -> Entry<T> {
match self
.map
.get_or_insert_with(FxHashMap::default)
.entry(TypeId::of::<T>())
{
hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry {
data: e,
marker: PhantomData,
}),
hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry {
data: e,
marker: PhantomData,
}),
}
}
}
/// Provides the same `TypeMap` container, but with `Send` + `Sync` bounds on values
pub mod concurrent {
use std::any::{Any, TypeId};
use rustc_hash::FxHashMap;
use std::collections::hash_map;
use std::marker::PhantomData;
/// Prepared key-value pair
pub struct KvPair(TypeId, Box<dyn Any + Send + Sync>);
impl KvPair {
pub fn new<T: 'static + Send + Sync>(value: T) -> Self {
KvPair(TypeId::of::<T>(), Box::new(value))
}
pub fn extract<T: 'static + Send + Sync>(self) -> Result<T, Self> {
let KvPair(key, value) = self;
if value.is::<T>() {
Ok((value as Box<dyn Any>)
.downcast()
.map(|boxed| *boxed)
.unwrap())
} else {
Err(KvPair(key, value))
}
}
}
/// A view into an occupied entry in a `TypeMap`.
#[derive(Debug)]
pub struct OccupiedEntry<'a, T> {
data: hash_map::OccupiedEntry<'a, TypeId, Box<dyn Any + Send + Sync>>,
marker: PhantomData<fn(T)>,
}
impl<'a, T: 'static + Send + Sync> OccupiedEntry<'a, T> {
/// Gets a reference to the value in the entry.
pub fn get(&self) -> &T {
self.data.get().downcast_ref().unwrap()
}
///Gets a mutable reference to the value in the entry.
pub fn get_mut(&mut self) -> &mut T {
self.data.get_mut().downcast_mut().unwrap()
}
/// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
/// with a lifetime bound to the map itself.
pub fn into_mut(self) -> &'a mut T {
self.data.into_mut().downcast_mut().unwrap()
}
/// Sets the value of the entry, and returns the entry's old value.
pub fn insert(&mut self, value: T) -> T {
(self.data.insert(Box::new(value)) as Box<dyn Any>)
.downcast()
.map(|boxed| *boxed)
.unwrap()
}
/// Takes the value out of the entry, and returns it.
pub fn remove(self) -> T {
(self.data.remove() as Box<dyn Any>)
.downcast()
.map(|boxed| *boxed)
.unwrap()
}
}
/// A view into a vacant entry in a `TypeMap`.
#[derive(Debug)]
pub struct VacantEntry<'a, T> {
data: hash_map::VacantEntry<'a, TypeId, Box<dyn Any + Send + Sync>>,
marker: PhantomData<fn(T)>,
}
impl<'a, T: 'static + Send + Sync> VacantEntry<'a, T> {
/// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it.
pub fn insert(self, value: T) -> &'a mut T {
self.data.insert(Box::new(value)).downcast_mut().unwrap()
}
}
/// A view into a single entry in a map, which may either be vacant or occupied.
#[derive(Debug)]
pub enum Entry<'a, T> {
Occupied(OccupiedEntry<'a, T>),
Vacant(VacantEntry<'a, T>),
}
impl<'a, T: 'static + Send + Sync> Entry<'a, T> {
/// Ensures a value is in the entry by inserting the default if empty, and returns
/// a mutable reference to the value in the entry.
pub fn or_insert(self, default: T) -> &'a mut T {
match self {
Entry::Occupied(inner) => inner.into_mut(),
Entry::Vacant(inner) => inner.insert(default),
}
}
/// Ensures a value is in the entry by inserting the result of the default function if empty, and returns
/// a mutable reference to the value in the entry.
pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T {
match self {
Entry::Occupied(inner) => inner.into_mut(),
Entry::Vacant(inner) => inner.insert(default()),
}
}
}
#[derive(Debug, Default)]
/// The typemap container
pub struct TypeMap {
map: Option<FxHashMap<TypeId, Box<dyn Any + Send + Sync>>>,
}
impl TypeMap {
/// Create an empty `TypeMap`.
#[inline]
pub fn new() -> Self {
Self { map: None }
}
/// Insert a prepared `KvPair` into this `TypeMap`.
///
/// If a value of this type already exists, it will be returned.
pub fn insert_kv_pair(&mut self, KvPair(key, value): KvPair) -> Option<KvPair> {
self.map
.get_or_insert_with(FxHashMap::default)
.insert(key, value)
.map(|old_value| KvPair(key, old_value))
}
/// Insert a value into this `TypeMap`.
///
/// If a value of this type already exists, it will be returned.
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
self.map
.get_or_insert_with(FxHashMap::default)
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed))
}
/// Check if container contains value for type
pub fn contains<T: 'static>(&self) -> bool {
self.map
.as_ref()
.and_then(|m| m.get(&TypeId::of::<T>()))
.is_some()
}
/// Get a reference to a value previously inserted on this `TypeMap`.
pub fn get<T: 'static>(&self) -> Option<&T> {
self.map
.as_ref()
.and_then(|m| m.get(&TypeId::of::<T>()))
.and_then(|boxed| boxed.downcast_ref())
}
/// Get a mutable reference to a value previously inserted on this `TypeMap`.
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.map
.as_mut()
.and_then(|m| m.get_mut(&TypeId::of::<T>()))
.and_then(|boxed| boxed.downcast_mut())
}
/// Remove a value from this `TypeMap`.
///
/// If a value of this type exists, it will be returned.
pub fn remove<T: 'static>(&mut self) -> Option<T> {
self.map
.as_mut()
.and_then(|m| m.remove(&TypeId::of::<T>()))
.and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed))
}
/// Clear the `TypeMap` of all inserted values.
#[inline]
pub fn clear(&mut self) {
self.map = None;
}
/// Get an entry in the `TypeMap` for in-place manipulation.
pub fn entry<T: 'static + Send + Sync>(&mut self) -> Entry<T> {
match self
.map
.get_or_insert_with(FxHashMap::default)
.entry(TypeId::of::<T>())
{
hash_map::Entry::Occupied(e) => Entry::Occupied(OccupiedEntry {
data: e,
marker: PhantomData,
}),
hash_map::Entry::Vacant(e) => Entry::Vacant(VacantEntry {
data: e,
marker: PhantomData,
}),
}
}
}
}
#[test]
fn test_type_map() {
#[derive(Debug, PartialEq)]
struct MyType(i32);
#[derive(Debug, PartialEq, Default)]
struct MyType2(String);
let mut map = TypeMap::new();
map.insert(5i32);
map.insert(MyType(10));
assert_eq!(map.get(), Some(&5i32));
assert_eq!(map.get_mut(), Some(&mut 5i32));
assert_eq!(map.remove::<i32>(), Some(5i32));
assert!(map.get::<i32>().is_none());
assert_eq!(map.get::<bool>(), None);
assert_eq!(map.get(), Some(&MyType(10)));
let entry = map.entry::<MyType2>();
let v = entry.or_insert_with(MyType2::default);
v.0 = "Hello".into();
assert_eq!(map.get(), Some(&MyType2("Hello".into())));
}