use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
#[derive(Clone, Default)]
pub struct ThemeExtensions {
map: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
}
impl ThemeExtensions {
pub fn new() -> Self {
Self::default()
}
pub fn insert<T: Any + Send + Sync>(&mut self, ext: T) {
self.map.insert(TypeId::of::<T>(), Arc::new(ext));
}
pub fn get<T: Any + Send + Sync>(&self) -> Option<&T> {
self.map.get(&TypeId::of::<T>())?.downcast_ref::<T>()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn len(&self) -> usize {
self.map.len()
}
}
impl fmt::Debug for ThemeExtensions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ThemeExtensions")
.field("len", &self.map.len())
.finish()
}
}
impl PartialEq for ThemeExtensions {
fn eq(&self, other: &Self) -> bool {
self.map.len() == other.map.len() && self.map.keys().all(|k| other.map.contains_key(k))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct Foo(u32);
#[derive(Debug, PartialEq)]
struct Bar(&'static str);
#[test]
fn insert_then_get_round_trips() {
let mut ext = ThemeExtensions::new();
ext.insert(Foo(42));
assert_eq!(ext.get::<Foo>(), Some(&Foo(42)));
}
#[test]
fn missing_type_is_none_not_a_panic() {
let ext = ThemeExtensions::new();
assert!(ext.get::<Foo>().is_none());
}
#[test]
fn distinct_types_coexist() {
let mut ext = ThemeExtensions::new();
ext.insert(Foo(1));
ext.insert(Bar("hi"));
assert_eq!(ext.get::<Foo>(), Some(&Foo(1)));
assert_eq!(ext.get::<Bar>(), Some(&Bar("hi")));
}
#[test]
fn re_insert_of_same_type_replaces() {
let mut ext = ThemeExtensions::new();
ext.insert(Foo(1));
ext.insert(Foo(2));
assert_eq!(ext.get::<Foo>(), Some(&Foo(2)));
assert_eq!(ext.len(), 1);
}
#[test]
fn empty_map_reports_empty() {
let ext = ThemeExtensions::new();
assert!(ext.is_empty());
assert_eq!(ext.len(), 0);
}
#[test]
fn clone_is_independent_and_shares_attached_values() {
let mut ext = ThemeExtensions::new();
ext.insert(Foo(7));
let cloned = ext.clone();
assert_eq!(cloned.get::<Foo>(), Some(&Foo(7)));
ext.insert(Foo(99));
assert_eq!(ext.get::<Foo>(), Some(&Foo(99)));
assert_eq!(cloned.get::<Foo>(), Some(&Foo(7)));
}
#[test]
fn equality_compares_attached_type_set() {
let mut a = ThemeExtensions::new();
a.insert(Foo(1));
let mut b = ThemeExtensions::new();
b.insert(Foo(999)); assert_eq!(a, b);
let mut c = ThemeExtensions::new();
c.insert(Bar("x")); assert_ne!(a, c);
}
#[test]
fn debug_reports_len_not_values() {
let mut ext = ThemeExtensions::new();
ext.insert(Foo(1));
let s = format!("{ext:?}");
assert!(s.contains("ThemeExtensions"));
assert!(s.contains('1'));
}
}