use std::any::{Any, TypeId};
use std::collections::HashMap;
trait ClonableAny: Any + Clone + Sized {}
#[derive(Default, Debug)]
pub struct Purse {
data: HashMap<TypeId, Vec<Box<dyn Any>>>,
counts: HashMap<TypeId, u64>,
}
impl Purse {
pub fn new() -> Self {
Self {
data: HashMap::default(),
counts: HashMap::default(),
}
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn iter(&self) -> Box<dyn Iterator<Item = &dyn Any> + '_> {
Box::new(self.data.values().flatten().map(|b| &**b))
}
pub fn get_all_of_type<T: Any>(&self) -> Vec<&T> {
let type_id = TypeId::of::<T>();
self.data.get(&type_id).map_or(Vec::new(), |elems| {
elems
.iter()
.filter_map(|el| el.downcast_ref::<T>())
.collect()
})
}
pub fn contains<T: Any + Eq>(&self, t: T) -> bool {
let type_id = TypeId::of::<T>();
let Some(elems) = self.data.get(&type_id) else {
return false;
};
for elem in elems {
if let Some(elem) = elem.downcast_ref::<T>() {
if elem == &t {
return true;
}
}
}
false
}
pub fn types(&self) -> Vec<TypeId> {
self.data.keys().cloned().collect()
}
pub fn count<T: Any>(&self) -> u64 {
let type_id = TypeId::of::<T>();
*self.counts.get(&type_id).unwrap_or(&0)
}
pub fn most_common_type(&self) -> Option<TypeId> {
self.counts.keys().max().copied()
}
pub fn insert<T: Any>(&mut self, elem: T) {
let type_id = TypeId::of::<T>();
self.data.entry(type_id).or_default().push(Box::new(elem));
*self.counts.entry(type_id).or_insert(0) += 1;
}
pub fn remove<T: Any + Eq>(&mut self, elem: T) -> bool {
let type_id = TypeId::of::<T>();
if let Some(elems) = self.data.get_mut(&type_id) {
if let Some(index) = elems
.iter()
.position(|el| el.downcast_ref::<T>() == Some(&elem))
{
elems.remove(index);
*self.counts.entry(type_id).or_insert(0) =
self.counts.entry(type_id).or_insert(0).saturating_sub(1);
return true;
}
}
false
}
pub fn clear(&mut self) {
self.data.clear();
self.counts.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_purse() {
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
enum RPS {
Rock,
Paper,
Scissors,
}
let mut purse = Purse::new();
purse.insert(5);
purse.insert("foo");
purse.insert(RPS::Rock);
purse.insert(RPS::Paper);
assert!(purse.contains("foo"));
assert!(!purse.contains("bar"));
assert!(!purse.contains(0));
assert!(purse.contains(RPS::Rock));
assert!(!purse.contains(RPS::Scissors));
purse.remove(RPS::Rock);
assert!(!purse.contains(RPS::Rock));
assert_eq!(purse.count::<&str>(), 1);
purse.insert("bar");
purse.insert("baz");
assert_eq!(purse.count::<&str>(), 3);
let types = purse.types();
assert_eq!(types.len(), 3);
assert!(types.contains(&TypeId::of::<i32>()));
assert!(types.contains(&TypeId::of::<&str>()));
assert!(types.contains(&TypeId::of::<RPS>()));
let strings: Vec<&&str> = purse.get_all_of_type();
assert_eq!(strings.len(), 3);
assert!(strings.contains(&&"foo"));
assert!(strings.contains(&&"bar"));
assert!(strings.contains(&&"baz"));
assert_eq!(purse.most_common_type(), Some(TypeId::of::<&str>()));
purse.clear();
assert!(purse.is_empty());
purse.insert(5);
purse.insert("foo");
purse.insert(RPS::Paper);
let mut nums: Vec<i32> = vec![];
let mut strs: Vec<&str> = vec![];
let mut moves: Vec<RPS> = vec![];
purse.iter().for_each(|item| {
if let Some(&t) = item.downcast_ref::<i32>() {
nums.push(t);
} else if let Some(&t) = item.downcast_ref::<&str>() {
strs.push(t);
} else if let Some(&t) = item.downcast_ref::<RPS>() {
moves.push(t);
} else {
panic!("unexpected type found in bag");
}
});
assert_eq!(nums.first(), Some(&5));
assert_eq!(strs.first(), Some(&"foo"));
assert_eq!(moves.first(), Some(&RPS::Paper));
}
}