#![forbid(unsafe_code)]
#![deny(missing_docs)]
use std::{
any::{Any, TypeId},
collections::HashMap,
fmt::Debug,
};
#[derive(Default)]
pub struct Extensions {
map: HashMap<TypeId, Box<dyn Any>>,
}
impl Extensions {
pub fn get<T: Any>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())
.and_then(|any| any.downcast_ref::<T>())
}
pub fn get_mut<T: Any>(&mut self) -> Option<&mut T> {
self.map
.get_mut(&TypeId::of::<T>())
.and_then(|any| any.downcast_mut::<T>())
}
pub fn insert<T: Any>(&mut self, value: T) {
self.map.insert(TypeId::of::<T>(), Box::new(value));
}
pub fn remove<T: Any>(&mut self) -> Option<T> {
self.map
.remove(&TypeId::of::<T>())
.and_then(|any| any.downcast().ok().map(|boxed| *boxed))
}
pub fn clear(&mut self) {
self.map.clear();
}
pub fn contains<T: Any>(&self) -> bool {
self.map.contains_key(&TypeId::of::<T>())
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn len(&self) -> usize {
self.map.len()
}
}
impl Debug for Extensions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Extensions").finish()
}
}
#[cfg(test)]
mod tests {
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use super::*;
#[test]
fn basic() {
let mut extensions = Extensions::default();
assert!(extensions.is_empty());
extensions.insert::<u32>(42);
assert_eq!(extensions.len(), 1);
let value = extensions.get::<u32>().unwrap();
assert_eq!(*value, 42);
let value = extensions.get_mut::<u32>().unwrap();
*value = 24;
assert_eq!(*extensions.get::<u32>().unwrap(), 24);
extensions.remove::<u32>().unwrap();
assert!(extensions.is_empty());
}
#[test]
fn drop_called() {
#[derive(Debug)]
struct DropCounter(Arc<AtomicUsize>);
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
let drop_counter = Arc::new(AtomicUsize::new(0));
{
let mut extensions = Extensions::default();
extensions.insert(DropCounter(drop_counter.clone()));
}
let count = drop_counter.load(Ordering::SeqCst);
assert_eq!(count, 1);
}
}