use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::collections::hash_map::{self, RandomState};
use std::fmt;
use std::hash::BuildHasher;
use std::marker::PhantomData;
pub struct TypeMap<S = RandomState> {
map: HashMap<TypeId, Box<Any>, S>,
}
impl TypeMap<RandomState> {
#[inline]
pub fn new() -> TypeMap {
TypeMap{
map: HashMap::new(),
}
}
#[inline]
pub fn with_capacity(n: usize) -> TypeMap {
TypeMap{
map: HashMap::with_capacity(n),
}
}
}
impl<S: BuildHasher> TypeMap<S> {
#[inline]
pub fn with_hasher(hash_builder: S) -> TypeMap<S> {
TypeMap{
map: HashMap::with_hasher(hash_builder),
}
}
#[inline]
pub fn clear(&mut self) {
self.map.clear();
}
#[inline]
pub fn contains<T: Any>(&self) -> bool {
self.map.contains_key(&TypeId::of::<T>())
}
#[inline]
pub fn capacity(&self) -> usize {
self.map.capacity()
}
#[inline]
pub fn entry<T: Any>(&mut self) -> Entry<T> {
match self.map.entry(TypeId::of::<T>()) {
hash_map::Entry::Vacant(ent) => Entry::Vacant(VacantEntry::new(ent)),
hash_map::Entry::Occupied(ent) => Entry::Occupied(OccupiedEntry::new(ent))
}
}
#[inline]
pub fn get<T: Any>(&self) -> Option<&T> {
if let Some(v) = self.map.get(&TypeId::of::<T>()) {
Some(v.downcast_ref().expect("wrong type in TypeMap"))
} else {
None
}
}
#[inline]
pub fn get_mut<T: Any>(&mut self) -> Option<&mut T> {
if let Some(v) = self.map.get_mut(&TypeId::of::<T>()) {
Some(v.downcast_mut().expect("wrong type in TypeMap"))
} else {
None
}
}
#[inline]
pub fn insert<T: Any>(&mut self, t: T) -> Option<T> {
self.map.insert(TypeId::of::<T>(), Box::new(t))
.map(|old| *old.downcast().expect("wrong type in TypeMap"))
}
#[inline]
pub fn len(&self) -> usize {
self.map.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.map.reserve(additional);
}
#[inline]
pub fn remove<T: Any>(&mut self) -> Option<T> {
self.map.remove(&TypeId::of::<T>())
.map(|old| *old.downcast().expect("wrong type in TypeMap"))
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.map.shrink_to_fit();
}
}
impl<S> fmt::Debug for TypeMap<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("TypeMap { .. }")
}
}
impl<S: BuildHasher + Default> Default for TypeMap<S> {
fn default() -> TypeMap<S> {
TypeMap::with_hasher(S::default())
}
}
pub enum Entry<'a, V: Any> {
Occupied(OccupiedEntry<'a, V>),
Vacant(VacantEntry<'a, V>),
}
impl<'a, V: Any> Entry<'a, V> {
#[inline]
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
Entry::Occupied(ent) => ent.into_mut(),
Entry::Vacant(ent) => ent.insert(default)
}
}
#[inline]
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
match self {
Entry::Occupied(ent) => ent.into_mut(),
Entry::Vacant(ent) => ent.insert(default())
}
}
}
impl<'a, V: Any + fmt::Debug> fmt::Debug for Entry<'a, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Entry::Occupied(ref ent) =>
f.debug_tuple("Entry")
.field(ent)
.finish(),
Entry::Vacant(ref ent) =>
f.debug_tuple("Entry")
.field(ent)
.finish(),
}
}
}
pub struct OccupiedEntry<'a, V: Any> {
entry: hash_map::OccupiedEntry<'a, TypeId, Box<Any>>,
_data: PhantomData<V>,
}
impl<'a, V: Any> OccupiedEntry<'a, V> {
fn new(entry: hash_map::OccupiedEntry<'a, TypeId, Box<Any>>) -> OccupiedEntry<'a, V> {
OccupiedEntry{
entry: entry,
_data: PhantomData,
}
}
#[inline]
pub fn get(&self) -> &V {
self.entry.get().downcast_ref().expect("wrong type in entry")
}
#[inline]
pub fn get_mut(&mut self) -> &mut V {
self.entry.get_mut().downcast_mut().expect("wrong type in entry")
}
#[inline]
pub fn into_mut(self) -> &'a mut V {
self.entry.into_mut().downcast_mut().expect("wrong type in entry")
}
#[inline]
pub fn insert(&mut self, value: V) -> V {
*self.entry.insert(Box::new(value)).downcast().expect("wrong type in entry")
}
#[inline]
pub fn remove(self) -> V {
*self.entry.remove().downcast().expect("wrong type in entry")
}
}
impl<'a, V: Any + fmt::Debug> fmt::Debug for OccupiedEntry<'a, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("OccupiedEntry")
.field(self.get())
.finish()
}
}
pub struct VacantEntry<'a, V: Any> {
entry: hash_map::VacantEntry<'a, TypeId, Box<Any>>,
_data: PhantomData<V>,
}
impl<'a, V: Any> VacantEntry<'a, V> {
fn new(entry: hash_map::VacantEntry<'a, TypeId, Box<Any>>) -> VacantEntry<'a, V> {
VacantEntry{
entry: entry,
_data: PhantomData,
}
}
#[inline]
pub fn insert(self, value: V) -> &'a mut V {
self.entry.insert(Box::new(value)).downcast_mut().expect("wrong type in entry")
}
}
impl<'a, V: Any> fmt::Debug for VacantEntry<'a, V> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("VacantEntry { .. }")
}
}
#[cfg(test)]
mod test {
use super::TypeMap;
#[test]
fn test_debug() {
let mut m = TypeMap::new();
m.insert(123_i32);
assert_eq!(format!("{:?}", m), "TypeMap { .. }");
assert_eq!(format!("{:?}", m.entry::<i32>()),
r#"Entry(OccupiedEntry(123))"#);
assert_eq!(format!("{:?}", m.entry::<()>()),
r#"Entry(VacantEntry { .. })"#);
}
#[test]
fn test_entry() {
let mut m = TypeMap::new();
m.entry::<u32>().or_insert(123);
m.entry::<String>().or_insert_with(String::new);
assert_eq!(m.len(), 2);
assert_eq!(m.get::<u32>(), Some(&123));
assert_eq!(m.get::<String>(), Some(&String::new()));
}
#[derive(Debug, Eq, PartialEq)]
struct Foo {
a: i32,
b: i32,
}
#[test]
fn test_typemap() {
let mut m = TypeMap::new();
m.insert(123);
m.insert(456_u32);
m.insert("foo");
m.insert(Foo{a: 1, b: 2});
assert_eq!(m.get::<i32>(), Some(&123));
assert_eq!(m.get::<u32>(), Some(&456));
assert_eq!(m.get::<&str>(), Some(&"foo"));
assert_eq!(m.get::<Foo>(), Some(&Foo{a: 1, b: 2}));
}
}