#![license = "MIT"]
#![deny(missing_docs)]
#![deny(warnings)]
extern crate alloc;
extern crate "unsafe-any" as uany;
use std::any::Any;
use std::intrinsics::TypeId;
use std::collections::{hash_map, HashMap};
use uany::{UncheckedAnyDowncast, UncheckedAnyMutDowncast, UncheckedBoxAnyDowncast};
use Entry::{Occupied, Vacant};
#[deriving(Default)]
pub struct TypeMap {
data: HashMap<TypeId, Box<Any + 'static>>
}
pub trait Assoc<Value: 'static>: 'static {}
impl TypeMap {
pub fn new() -> TypeMap {
TypeMap {
data: HashMap::new()
}
}
pub fn insert<K: Assoc<V>, V: 'static>(&mut self, val: V) -> Option<V> {
self.data.insert(TypeId::of::<K>(), box val as Box<Any>).map(|v| unsafe {
*v.downcast_unchecked::<V>()
})
}
#[deprecated = "renamed to `get`"]
pub fn find<K: Assoc<V>, V: 'static>(&self) -> Option<&V> {
self.data.get(&TypeId::of::<K>()).map(|v| unsafe {
v.downcast_ref_unchecked::<V>()
})
}
#[deprecated = "renamed to `get_mut`"]
pub fn find_mut<K: Assoc<V>, V: 'static>(&mut self) -> Option<&mut V> {
self.data.get_mut(&TypeId::of::<K>()).map(|v| unsafe {
v.downcast_mut_unchecked::<V>()
})
}
pub fn get<K: Assoc<V>, V: 'static>(&self) -> Option<&V> {
self.data.get(&TypeId::of::<K>()).map(|v| unsafe {
v.downcast_ref_unchecked::<V>()
})
}
pub fn get_mut<K: Assoc<V>, V: 'static>(&mut self) -> Option<&mut V> {
self.data.get_mut(&TypeId::of::<K>()).map(|v| unsafe {
v.downcast_mut_unchecked::<V>()
})
}
pub fn contains<K: Assoc<V>, V: 'static>(&self) -> bool {
self.data.contains_key(&TypeId::of::<K>())
}
pub fn remove<K: Assoc<V>, V: 'static>(&mut self) -> Option<V> {
self.data.remove(&TypeId::of::<K>()).map(|v| unsafe {
*v.downcast_unchecked::<V>()
})
}
pub fn entry<'a, K: Assoc<V>, V: 'static>(&'a mut self) -> Entry<'a, K, V> {
match self.data.entry(TypeId::of::<K>()) {
hash_map::Occupied(e) => Occupied(OccupiedEntry { data: e }),
hash_map::Vacant(e) => Vacant(VacantEntry { data: e })
}
}
pub unsafe fn data(&self) -> &HashMap<TypeId, Box<Any + 'static>> { &self.data }
pub unsafe fn data_mut(&mut self) -> &mut HashMap<TypeId, Box<Any + 'static>> { &mut self.data }
pub fn len(&self) -> uint {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn clear(&mut self) {
self.data.clear()
}
}
pub enum Entry<'a, K, V> {
Occupied(OccupiedEntry<'a, K, V>),
Vacant(VacantEntry<'a, K, V>)
}
pub struct OccupiedEntry<'a, K, V> {
data: hash_map::OccupiedEntry<'a, TypeId, Box<Any + 'static>>
}
pub struct VacantEntry<'a, K, V> {
data: hash_map::VacantEntry<'a, TypeId, Box<Any + 'static>>
}
impl<'a, K, V: 'static> OccupiedEntry<'a, K, V> {
pub fn get(&self) -> &V {
unsafe {
self.data.get().downcast_ref_unchecked::<V>()
}
}
pub fn get_mut(&mut self) -> &mut V {
unsafe {
self.data.get_mut().downcast_mut_unchecked::<V>()
}
}
pub fn into_mut(self) -> &'a mut V {
unsafe {
self.data.into_mut().downcast_mut_unchecked::<V>()
}
}
pub fn set(&mut self, value: V) -> V {
unsafe {
*self.data.set(box value as Box<Any + 'static>).downcast_unchecked::<V>()
}
}
pub fn take(self) -> V {
unsafe {
*self.data.take().downcast_unchecked::<V>()
}
}
}
impl<'a, K, V: 'static> VacantEntry<'a, K, V> {
pub fn set(self, value: V) -> &'a mut V {
unsafe {
self.data.set(box value as Box<Any + 'static>).downcast_mut_unchecked::<V>()
}
}
}
#[cfg(test)]
mod test {
use super::{TypeMap, Assoc};
use super::Entry::{Occupied, Vacant};
#[deriving(Show, PartialEq)]
struct Key;
#[deriving(Show, PartialEq)]
struct Value;
impl Assoc<Value> for Key {}
#[test] fn test_pairing() {
let mut map = TypeMap::new();
map.insert::<Key, Value>(Value);
assert_eq!(*map.get::<Key, Value>().unwrap(), Value);
assert!(map.contains::<Key, Value>());
}
#[test] fn test_remove() {
let mut map = TypeMap::new();
map.insert::<Key, Value>(Value);
assert!(map.contains::<Key, Value>());
map.remove::<Key, Value>();
assert!(!map.contains::<Key, Value>());
}
#[test] fn test_entry() {
let mut map = TypeMap::new();
map.insert::<Key, Value>(Value);
match map.entry::<Key, Value>() {
Occupied(e) => {
assert_eq!(e.get(), &Value);
assert_eq!(e.take(), Value);
},
_ => panic!("Unable to locate inserted item.")
}
assert!(!map.contains::<Key, Value>());
match map.entry::<Key, Value>() {
Vacant(e) => {
e.set(Value);
},
_ => panic!("Found non-existant entry.")
}
assert!(map.contains::<Key, Value>());
}
}