//
// rustc 1.60.0-nightly (b17226fcc 2022-02-18)
//
#![feature(box_into_inner)]
use std::any::{Any, TypeId};
use std::cmp::Eq;
use std::collections::HashMap;
use std::hash::Hash;
type HashKey<K> = (K, TypeId);
type Anything = Box<dyn Any>;
pub struct AnyMap<K: Eq + Hash>(HashMap<HashKey<K>, Anything>);
impl<K: Eq + Hash> AnyMap<K> {
/// Creates a new hashmap that can store
/// any data which can be tagged with the
/// `Any` trait.
pub fn new() -> Self {
Self(HashMap::new())
}
/// Creates a new hashmap that can store
/// at least the capacity given.
pub fn new_with_capacity(capacity: usize) -> Self {
Self(HashMap::with_capacity(capacity))
}
/// Inserts the provided value under the key. Keys
/// are tracked with their type; meaning you can
/// have the same key used multiple times with different
/// values.
///
/// If the storage had a value of the type being stored
/// under the same key it is returned.
pub fn insert<V: Any>(&mut self, key: K, val: V) -> Option<V> {
let boxed = self
.0
.insert((key, val.type_id()), Box::new(val))?
.downcast::<V>()
.ok()?;
Some(Box::into_inner(boxed))
}
/// Fetch a reference for the type given under a
/// given key. Note that the key needs to be provided
/// with ownership. This may change in the future if
/// I can figure out how to only borrow the key for
/// comparison.
pub fn get<V: Any>(&self, key: K) -> Option<&V> {
self.0.get(&(key, TypeId::of::<V>()))?.downcast_ref::<V>()
}
/// A mutable reference for the type given under
/// a given key. Note that the key needs to be provided
/// with ownership.
pub fn get_mut<V: Any>(&mut self, key: K) -> Option<&mut V> {
self.0
.get_mut(&(key, TypeId::of::<V>()))?
.downcast_mut::<V>()
}
/// Removes the data of the given type under they key
/// if it's found. The data found is returned in an
/// Option after it's removed.
pub fn remove<V: Any>(&mut self, key: K) -> Option<V> {
let boxed = self
.0
.remove(&(key, TypeId::of::<V>()))?
.downcast::<V>()
.ok()?;
Some(Box::into_inner(boxed))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn you_can_add_different_types_and_work_with_them() {
let mut storage = AnyMap::new();
storage.insert("pie", 3.142);
storage.insert("pie", "apple");
assert_eq!(
&3.142,
storage.get::<f64>("pie").unwrap()
);
assert_eq!(
&"apple",
storage.get::<&str>("pie").unwrap()
);
*storage.get_mut("pie").unwrap() = 3.14159;
assert_eq!(
&3.14159,
storage.get::<f64>("pie").unwrap()
);
assert_eq!(
None,
storage.get::<f32>("pie")
);
assert_eq!(
3.14159,
storage.remove::<f64>("pie").unwrap()
);
assert_eq!(
None,
storage.get::<f64>("pie")
);
}
}