use dyn_clone::DynClone;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::Debug;
trait Data: Any + DynClone {}
dyn_clone::clone_trait_object!(Data);
impl<T: Clone + 'static> Data for T {}
#[derive(Clone)]
struct Entry(Box<dyn Data>);
impl std::fmt::Debug for Entry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Entry").finish_non_exhaustive()
}
}
#[derive(Clone, Debug, Default)]
pub struct DataSet {
inner: HashMap<TypeId, Entry>,
}
impl DataSet {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn insert<T: Clone + 'static>(&mut self, data: Box<T>) -> Option<Box<T>> {
self.inner
.insert(TypeId::of::<T>(), Entry(data))
.map(|old| (old.0 as Box<dyn Any>).downcast().unwrap())
}
#[must_use]
pub fn get<T: 'static>(&self) -> Option<&T> {
self.inner
.get(&TypeId::of::<T>())
.map(|entry| (&*entry.0 as &dyn Any).downcast_ref().unwrap())
}
#[must_use]
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.inner
.get_mut(&TypeId::of::<T>())
.map(|entry| (&mut *entry.0 as &mut dyn Any).downcast_mut().unwrap())
}
pub fn get_or_insert_with<T, F>(&mut self, f: F) -> &mut T
where
T: Clone + 'static,
F: FnOnce() -> Box<T>,
{
let entry = self
.inner
.entry(TypeId::of::<T>())
.or_insert_with(|| Entry(f()));
(&mut *entry.0 as &mut dyn Any).downcast_mut().unwrap()
}
pub fn remove<T: 'static>(&mut self) -> Option<Box<T>> {
self.inner
.remove(&TypeId::of::<T>())
.map(|entry| (entry.0 as Box<dyn Any>).downcast().unwrap())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_and_get() {
let mut data_set = DataSet::new();
let old = data_set.insert(Box::new(42i32));
assert_eq!(old, None);
assert_eq!(data_set.get::<i32>(), Some(&42));
}
#[test]
fn insert_again() {
let mut data_set = DataSet::new();
data_set.insert(Box::new(42i32));
let old = data_set.insert(Box::new(43i32));
assert_eq!(old, Some(Box::new(42)));
assert_eq!(data_set.get::<i32>(), Some(&43));
}
#[test]
fn get_mut() {
let mut data_set = DataSet::new();
data_set.insert(Box::new(42i32));
let data = data_set.get_mut::<i32>().unwrap();
assert_eq!(data, &42);
*data = 43;
assert_eq!(data_set.get::<i32>(), Some(&43));
}
#[test]
fn get_or_insert_with() {
let mut data_set = DataSet::new();
let data = data_set.get_or_insert_with(|| Box::new(0i8));
assert_eq!(data, &0);
*data = 1;
let data = data_set.get_or_insert_with::<i8, _>(|| unreachable!());
assert_eq!(data, &1);
}
#[test]
fn remove_existing() {
let mut data_set = DataSet::new();
data_set.insert(Box::new(42i32));
let data = data_set.remove::<i32>().unwrap();
assert_eq!(*data, 42);
}
#[test]
fn remove_nonexisting() {
let mut data_set = DataSet::new();
let data = data_set.remove::<i32>();
assert_eq!(data, None);
}
#[test]
fn clone() {
let mut data_set = DataSet::new();
data_set.insert::<i32>(Box::new(42));
let clone = data_set.clone();
assert_eq!(clone.get::<i32>(), Some(&42));
}
}