use std::any::{Any, TypeId};
use std::collections::HashMap;
struct CloneableEntry {
value: Box<dyn Any + Send + Sync>,
clone_fn: fn(&(dyn Any + Send + Sync)) -> Box<dyn Any + Send + Sync>,
}
fn make_clone_fn<T: Clone + Send + Sync + 'static>()
-> fn(&(dyn Any + Send + Sync)) -> Box<dyn Any + Send + Sync> {
|any| {
let val = match any.downcast_ref::<T>() {
Some(v) => v,
None => {
unreachable!(
"TypedMetadata clone: type mismatch for TypeId {:?}",
std::any::TypeId::of::<T>()
);
}
};
Box::new(val.clone())
}
}
impl Clone for CloneableEntry {
fn clone(&self) -> Self {
Self {
value: (self.clone_fn)(&*self.value),
clone_fn: self.clone_fn,
}
}
}
pub struct TypedMetadata {
map: HashMap<TypeId, CloneableEntry>,
}
impl TypedMetadata {
#[must_use]
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
pub fn insert<T: Clone + Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
let entry = CloneableEntry {
value: Box::new(val),
clone_fn: make_clone_fn::<T>(),
};
self.map
.insert(TypeId::of::<T>(), entry)
.and_then(|old| old.value.downcast::<T>().ok())
.map(|b| *b)
}
#[must_use]
pub fn get<T: 'static>(&self) -> Option<&T> {
let entry = self.map.get(&TypeId::of::<T>())?;
entry.value.downcast_ref()
}
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
let entry = self.map.get_mut(&TypeId::of::<T>())?;
entry.value.downcast_mut()
}
pub fn remove<T: 'static>(&mut self) -> Option<T> {
self.map
.remove(&TypeId::of::<T>())
.and_then(|old| old.value.downcast::<T>().ok())
.map(|b| *b)
}
#[must_use]
pub fn contains<T: 'static>(&self) -> bool {
self.map.contains_key(&TypeId::of::<T>())
}
#[must_use]
pub fn len(&self) -> usize {
self.map.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn merge(&mut self, other: TypedMetadata) {
self.map.extend(other.map);
}
}
impl Clone for TypedMetadata {
fn clone(&self) -> Self {
Self {
map: self.map.clone(),
}
}
}
impl Default for TypedMetadata {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for TypedMetadata {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TypedMetadata")
.field("len", &self.map.len())
.finish()
}
}
use std::fmt;
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug, PartialEq)]
struct Foo(u32);
#[derive(Clone, Debug, PartialEq)]
struct Bar(String);
#[test]
fn insert_and_get() {
let mut m = TypedMetadata::new();
m.insert(Foo(42));
assert_eq!(m.get::<Foo>(), Some(&Foo(42)));
assert_eq!(m.get::<Bar>(), None);
}
#[test]
fn replace_returns_old() {
let mut m = TypedMetadata::new();
assert!(m.insert(Foo(1)).is_none());
let old = m.insert(Foo(2));
assert_eq!(old, Some(Foo(1)));
assert_eq!(m.get::<Foo>(), Some(&Foo(2)));
}
#[test]
fn remove() {
let mut m = TypedMetadata::new();
m.insert(Foo(10));
let removed = m.remove::<Foo>();
assert_eq!(removed, Some(Foo(10)));
assert!(!m.contains::<Foo>());
}
#[test]
fn clone_is_deep() {
let mut m = TypedMetadata::new();
m.insert(Bar("hello".into()));
let m2 = m.clone();
m.insert(Bar("changed".into()));
assert_eq!(m2.get::<Bar>(), Some(&Bar("hello".into())));
}
#[test]
fn merge_overwrites() {
let mut a = TypedMetadata::new();
a.insert(Foo(1));
let mut b = TypedMetadata::new();
b.insert(Foo(2));
b.insert(Bar("from_b".into()));
a.merge(b);
assert_eq!(a.get::<Foo>(), Some(&Foo(2)));
assert_eq!(a.get::<Bar>(), Some(&Bar("from_b".into())));
}
}