use std::any::{Any, TypeId};
use std::collections::HashMap;
type BoxedAny = Box<dyn Any + Send + Sync>;
#[derive(Default)]
pub struct Extensions {
map: Option<HashMap<TypeId, BoxedAny>>,
}
impl std::fmt::Debug for Extensions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Extensions")
.field("len", &self.len())
.finish_non_exhaustive()
}
}
impl Extensions {
pub fn new() -> Self {
Self { map: None }
}
pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
let map = self.map.get_or_insert_with(HashMap::new);
map.insert(TypeId::of::<T>(), Box::new(value))
.and_then(|boxed| boxed.downcast::<T>().ok().map(|b| *b))
}
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.map
.as_ref()?
.get(&TypeId::of::<T>())
.and_then(|b| b.downcast_ref::<T>())
}
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
self.map
.as_mut()?
.get_mut(&TypeId::of::<T>())
.and_then(|b| b.downcast_mut::<T>())
}
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.map
.as_mut()?
.remove(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast::<T>().ok().map(|b| *b))
}
pub fn len(&self) -> usize {
self.map.as_ref().map_or(0, |m| m.len())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
if let Some(m) = self.map.as_mut() {
m.clear();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct Marker(&'static str);
#[test]
fn roundtrip() {
let mut ext = Extensions::new();
assert!(ext.is_empty());
ext.insert(Marker("hi"));
assert_eq!(ext.len(), 1);
assert_eq!(ext.get::<Marker>(), Some(&Marker("hi")));
}
#[test]
fn overwrites_same_type() {
let mut ext = Extensions::new();
assert_eq!(ext.insert(Marker("first")), None);
assert_eq!(ext.insert(Marker("second")), Some(Marker("first")));
assert_eq!(ext.get::<Marker>(), Some(&Marker("second")));
}
#[test]
fn different_types_coexist() {
#[derive(Debug, PartialEq)]
struct Alpha(u32);
#[derive(Debug, PartialEq)]
struct Beta(String);
let mut ext = Extensions::new();
ext.insert(Alpha(1));
ext.insert(Beta("b".into()));
assert_eq!(ext.get::<Alpha>(), Some(&Alpha(1)));
assert_eq!(ext.get::<Beta>(), Some(&Beta("b".into())));
assert_eq!(ext.len(), 2);
}
#[test]
fn remove_returns_ownership() {
let mut ext = Extensions::new();
ext.insert(Marker("x"));
assert_eq!(ext.remove::<Marker>(), Some(Marker("x")));
assert!(ext.get::<Marker>().is_none());
}
#[test]
fn get_mut_is_mutable() {
let mut ext = Extensions::new();
ext.insert(vec![1u32, 2, 3]);
ext.get_mut::<Vec<u32>>().unwrap().push(4);
assert_eq!(ext.get::<Vec<u32>>().unwrap(), &vec![1, 2, 3, 4]);
}
#[test]
fn send_sync_bound_holds() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Extensions>();
}
}