use std::any::{Any, TypeId};
use std::collections::HashMap;
pub struct Extensions {
map: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
}
impl Extensions {
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
self.map.insert(TypeId::of::<T>(), Box::new(value));
}
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_ref())
}
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
self.map
.get_mut(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast_mut())
}
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
self.map
.remove(&TypeId::of::<T>())
.and_then(|boxed| boxed.downcast().ok())
.map(|boxed| *boxed)
}
pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
self.map.contains_key(&TypeId::of::<T>())
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
}
impl Default for Extensions {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestResource {
value: i32,
}
struct AnotherResource {
name: String,
}
#[test]
fn test_insert_and_get() {
let mut extensions = Extensions::new();
extensions.insert(TestResource { value: 42 });
let resource = extensions.get::<TestResource>().unwrap();
assert_eq!(resource.value, 42);
}
#[test]
fn test_get_mut() {
let mut extensions = Extensions::new();
extensions.insert(TestResource { value: 10 });
{
let resource = extensions.get_mut::<TestResource>().unwrap();
resource.value = 20;
}
assert_eq!(extensions.get::<TestResource>().unwrap().value, 20);
}
#[test]
fn test_remove() {
let mut extensions = Extensions::new();
extensions.insert(TestResource { value: 100 });
let removed = extensions.remove::<TestResource>().unwrap();
assert_eq!(removed.value, 100);
assert!(extensions.get::<TestResource>().is_none());
}
#[test]
fn test_contains() {
let mut extensions = Extensions::new();
assert!(!extensions.contains::<TestResource>());
extensions.insert(TestResource { value: 0 });
assert!(extensions.contains::<TestResource>());
}
#[test]
fn test_multiple_types() {
let mut extensions = Extensions::new();
extensions.insert(TestResource { value: 1 });
extensions.insert(AnotherResource {
name: "test".to_string(),
});
assert_eq!(extensions.len(), 2);
assert_eq!(extensions.get::<TestResource>().unwrap().value, 1);
assert_eq!(extensions.get::<AnotherResource>().unwrap().name, "test");
}
#[test]
fn test_type_mismatch_returns_none() {
let mut extensions = Extensions::new();
extensions.insert(TestResource { value: 42 });
assert!(extensions.get::<AnotherResource>().is_none());
}
}