1use std::any::{Any, TypeId};
6use std::collections::HashMap;
7use std::fmt;
8use std::hash::{BuildHasherDefault, Hasher};
9
10#[derive(Default)]
17pub struct Extensions {
18 map: Option<HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>>,
19}
20
21impl Extensions {
22 #[inline]
24 pub(crate) fn new() -> Self {
25 Self { map: None }
26 }
27
28 pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
32 self.map
33 .get_or_insert_with(Default::default)
34 .insert(TypeId::of::<T>(), Box::new(val))
35 .and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed))
36 }
37
38 pub fn contains<T: 'static>(&self) -> bool {
40 self.map
41 .as_ref()
42 .and_then(|m| m.get(&TypeId::of::<T>()))
43 .is_some()
44 }
45
46 pub fn get<T: 'static>(&self) -> Option<&T> {
48 self.map
49 .as_ref()
50 .and_then(|m| m.get(&TypeId::of::<T>()))
51 .and_then(|boxed| (&**boxed as &(dyn Any)).downcast_ref())
52 }
53
54 pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
56 self.map
57 .as_mut()
58 .and_then(|m| m.get_mut(&TypeId::of::<T>()))
59 .and_then(|boxed| (&mut **boxed as &mut (dyn Any)).downcast_mut())
60 }
61
62 pub fn remove<T: 'static>(&mut self) -> Option<T> {
66 self.map
67 .as_mut()
68 .and_then(|m| m.remove(&TypeId::of::<T>()))
69 .and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed))
70 }
71
72 #[inline]
74 pub fn clear(&mut self) {
75 self.map = None;
76 }
77}
78
79impl fmt::Debug for Extensions {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 f.debug_struct("Extensions").finish()
82 }
83}
84
85#[derive(Default)]
87struct IdHasher(u64);
88
89impl Hasher for IdHasher {
90 fn write(&mut self, _: &[u8]) {
91 unreachable!("TypeId calls write_u64");
92 }
93
94 #[inline]
95 fn write_u64(&mut self, id: u64) {
96 self.0 = id;
97 }
98
99 #[inline]
100 fn finish(&self) -> u64 {
101 self.0
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 #[test]
109 fn test_extensions() {
110 #[derive(Debug, PartialEq)]
111 struct MyType(i32);
112
113 let mut map = Extensions::new();
114
115 map.insert(5i32);
116 map.insert(MyType(10));
117
118 assert_eq!(map.get(), Some(&5i32));
119 assert_eq!(map.get_mut(), Some(&mut 5i32));
120
121 assert_eq!(map.remove::<i32>(), Some(5i32));
122 assert!(map.get::<i32>().is_none());
123
124 assert_eq!(map.get::<bool>(), None);
125 assert_eq!(map.get(), Some(&MyType(10)));
126 }
127}