1use std::any::{Any, TypeId};
2
3use rustc_hash::FxHashMap;
4
5use std::collections::hash_map;
6use std::marker::PhantomData;
7
8/// A view into an occupied entry in a `TypeMap`.
9#[derive(Debug)]
10pub struct OccupiedEntry<'a, T> {
11 data: hash_map::OccupiedEntry<'a, TypeId, Box<dyn Any>>,
12 marker: PhantomData<fn(T)>,
13}
14
15impl<'a, T: 'static> OccupiedEntry<'a, T> {
16 /// Gets a reference to the value in the entry.
17 pub fn get(&self) -> &T {
18 self.data.get().downcast_ref().unwrap()
19 }
20
21 ///Gets a mutable reference to the value in the entry.
22 pub fn get_mut(&mut self) -> &mut T {
23 self.data.get_mut().downcast_mut().unwrap()
24 }
25
26 /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
27 /// with a lifetime bound to the map itself.
28 pub fn into_mut(self) -> &'a mut T {
29 self.data.into_mut().downcast_mut().unwrap()
30 }
31
32 /// Sets the value of the entry, and returns the entry's old value.
33 pub fn insert(&mut self, value: T) -> T {
34 self.data.insert(Box::new(value)).downcast().map(|boxed| *boxed).unwrap()
35 }
36
37 /// Takes the value out of the entry, and returns it.
38 pub fn remove(self) -> T {
39 self.data.remove().downcast().map(|boxed| *boxed).unwrap()
40 }
41}
42
43/// A view into a vacant entry in a `TypeMap`.
44#[derive(Debug)]
45pub struct VacantEntry<'a, T> {
46 data: hash_map::VacantEntry<'a, TypeId, Box<dyn Any>>,
47 marker: PhantomData<fn(T)>,
48}
49
50impl<'a, T: 'static> VacantEntry<'a, T> {
51 /// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it.
52 pub fn insert(self, value: T) -> &'a mut T {
53 self.data.insert(Box::new(value)).downcast_mut().unwrap()
54 }
55}
56
57/// A view into a single entry in a map, which may either be vacant or occupied.
58#[derive(Debug)]
59pub enum Entry<'a, T> {
60 Occupied(OccupiedEntry<'a, T>),
61 Vacant(VacantEntry<'a, T>),
62}
63
64impl<'a, T: 'static> Entry<'a, T> {
65 /// Ensures a value is in the entry by inserting the default if empty, and returns
66 /// a mutable reference to the value in the entry.
67 pub fn or_insert(self, default: T) -> &'a mut T {
68 match self {
69 Entry::Occupied(inner: OccupiedEntry<'_, T>) => inner.into_mut(),
70 Entry::Vacant(inner: VacantEntry<'_, T>) => inner.insert(default),
71 }
72 }
73
74 /// Ensures a value is in the entry by inserting the result of the default function if empty, and returns
75 /// a mutable reference to the value in the entry.
76 pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T {
77 match self {
78 Entry::Occupied(inner: OccupiedEntry<'_, T>) => inner.into_mut(),
79 Entry::Vacant(inner: VacantEntry<'_, T>) => inner.insert(default()),
80 }
81 }
82}
83
84#[derive(Debug, Default)]
85/// The typemap container
86pub struct TypeMap {
87 map: Option<FxHashMap<TypeId, Box<dyn Any>>>,
88}
89
90impl TypeMap {
91 /// Create an empty `TypeMap`.
92 #[inline]
93 pub fn new() -> Self {
94 Self { map: None }
95 }
96
97 /// Insert a value into this `TypeMap`.
98 ///
99 /// If a value of this type already exists, it will be returned.
100 pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> {
101 self.map
102 .get_or_insert_with(|| FxHashMap::default())
103 .insert(TypeId::of::<T>(), Box::new(val))
104 .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
105 }
106
107 /// Check if container contains value for type
108 pub fn contains<T: 'static>(&self) -> bool {
109 self.map.as_ref().and_then(|m| m.get(&TypeId::of::<T>())).is_some()
110 }
111
112 /// Get a reference to a value previously inserted on this `TypeMap`.
113 pub fn get<T: 'static>(&self) -> Option<&T> {
114 self.map
115 .as_ref()
116 .and_then(|m| m.get(&TypeId::of::<T>()))
117 .and_then(|boxed| boxed.downcast_ref())
118 }
119
120 /// Get a mutable reference to a value previously inserted on this `TypeMap`.
121 pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
122 self.map
123 .as_mut()
124 .and_then(|m| m.get_mut(&TypeId::of::<T>()))
125 .and_then(|boxed| boxed.downcast_mut())
126 }
127
128 /// Remove a value from this `TypeMap`.
129 ///
130 /// If a value of this type exists, it will be returned.
131 pub fn remove<T: 'static>(&mut self) -> Option<T> {
132 self.map
133 .as_mut()
134 .and_then(|m| m.remove(&TypeId::of::<T>()))
135 .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed))
136 }
137
138 /// Clear the `TypeMap` of all inserted values.
139 #[inline]
140 pub fn clear(&mut self) {
141 self.map = None;
142 }
143
144 /// Get an entry in the `TypeMap` for in-place manipulation.
145 pub fn entry<T: 'static>(&mut self) -> Entry<T> {
146 match self.map.get_or_insert_with(|| FxHashMap::default()).entry(TypeId::of::<T>()) {
147 hash_map::Entry::Occupied(e) => {
148 Entry::Occupied(OccupiedEntry { data: e, marker: PhantomData })
149 }
150 hash_map::Entry::Vacant(e) => {
151 Entry::Vacant(VacantEntry { data: e, marker: PhantomData })
152 }
153 }
154 }
155}
156
157/// Provides the same `TypeMap` container, but with `Send` + `Sync` bounds on values
158pub mod concurrent {
159
160 use std::any::{Any, TypeId};
161
162 use rustc_hash::FxHashMap;
163
164 use std::collections::hash_map;
165 use std::marker::PhantomData;
166
167 /// A view into an occupied entry in a `TypeMap`.
168 #[derive(Debug)]
169 pub struct OccupiedEntry<'a, T> {
170 data: hash_map::OccupiedEntry<'a, TypeId, Box<dyn Any + Send + Sync>>,
171 marker: PhantomData<fn(T)>,
172 }
173
174 impl<'a, T: 'static + Send + Sync> OccupiedEntry<'a, T> {
175 /// Gets a reference to the value in the entry.
176 pub fn get(&self) -> &T {
177 self.data.get().downcast_ref().unwrap()
178 }
179
180 ///Gets a mutable reference to the value in the entry.
181 pub fn get_mut(&mut self) -> &mut T {
182 self.data.get_mut().downcast_mut().unwrap()
183 }
184
185 /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
186 /// with a lifetime bound to the map itself.
187 pub fn into_mut(self) -> &'a mut T {
188 self.data.into_mut().downcast_mut().unwrap()
189 }
190
191 /// Sets the value of the entry, and returns the entry's old value.
192 pub fn insert(&mut self, value: T) -> T {
193 (self.data.insert(Box::new(value)) as Box<dyn Any>)
194 .downcast()
195 .map(|boxed| *boxed)
196 .unwrap()
197 }
198
199 /// Takes the value out of the entry, and returns it.
200 pub fn remove(self) -> T {
201 (self.data.remove() as Box<dyn Any>).downcast().map(|boxed| *boxed).unwrap()
202 }
203 }
204
205 /// A view into a vacant entry in a `TypeMap`.
206 #[derive(Debug)]
207 pub struct VacantEntry<'a, T> {
208 data: hash_map::VacantEntry<'a, TypeId, Box<dyn Any + Send + Sync>>,
209 marker: PhantomData<fn(T)>,
210 }
211
212 impl<'a, T: 'static + Send + Sync> VacantEntry<'a, T> {
213 /// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it.
214 pub fn insert(self, value: T) -> &'a mut T {
215 self.data.insert(Box::new(value)).downcast_mut().unwrap()
216 }
217 }
218
219 /// A view into a single entry in a map, which may either be vacant or occupied.
220 #[derive(Debug)]
221 pub enum Entry<'a, T> {
222 Occupied(OccupiedEntry<'a, T>),
223 Vacant(VacantEntry<'a, T>),
224 }
225
226 impl<'a, T: 'static + Send + Sync> Entry<'a, T> {
227 /// Ensures a value is in the entry by inserting the default if empty, and returns
228 /// a mutable reference to the value in the entry.
229 pub fn or_insert(self, default: T) -> &'a mut T {
230 match self {
231 Entry::Occupied(inner) => inner.into_mut(),
232 Entry::Vacant(inner) => inner.insert(default),
233 }
234 }
235
236 /// Ensures a value is in the entry by inserting the result of the default function if empty, and returns
237 /// a mutable reference to the value in the entry.
238 pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T {
239 match self {
240 Entry::Occupied(inner) => inner.into_mut(),
241 Entry::Vacant(inner) => inner.insert(default()),
242 }
243 }
244 }
245
246 #[derive(Debug, Default)]
247 /// The typemap container
248 pub struct TypeMap {
249 map: Option<FxHashMap<TypeId, Box<dyn Any + Send + Sync>>>,
250 }
251
252 impl TypeMap {
253 /// Create an empty `TypeMap`.
254 #[inline]
255 pub fn new() -> Self {
256 Self { map: None }
257 }
258
259 /// Insert a value into this `TypeMap`.
260 ///
261 /// If a value of this type already exists, it will be returned.
262 pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
263 self.map
264 .get_or_insert_with(|| FxHashMap::default())
265 .insert(TypeId::of::<T>(), Box::new(val))
266 .and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed))
267 }
268
269 /// Check if container contains value for type
270 pub fn contains<T: 'static>(&self) -> bool {
271 self.map.as_ref().and_then(|m| m.get(&TypeId::of::<T>())).is_some()
272 }
273
274 /// Get a reference to a value previously inserted on this `TypeMap`.
275 pub fn get<T: 'static>(&self) -> Option<&T> {
276 self.map
277 .as_ref()
278 .and_then(|m| m.get(&TypeId::of::<T>()))
279 .and_then(|boxed| boxed.downcast_ref())
280 }
281
282 /// Get a mutable reference to a value previously inserted on this `TypeMap`.
283 pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
284 self.map
285 .as_mut()
286 .and_then(|m| m.get_mut(&TypeId::of::<T>()))
287 .and_then(|boxed| boxed.downcast_mut())
288 }
289
290 /// Remove a value from this `TypeMap`.
291 ///
292 /// If a value of this type exists, it will be returned.
293 pub fn remove<T: 'static>(&mut self) -> Option<T> {
294 self.map
295 .as_mut()
296 .and_then(|m| m.remove(&TypeId::of::<T>()))
297 .and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed))
298 }
299
300 /// Clear the `TypeMap` of all inserted values.
301 #[inline]
302 pub fn clear(&mut self) {
303 self.map = None;
304 }
305
306 /// Get an entry in the `TypeMap` for in-place manipulation.
307 pub fn entry<T: 'static + Send + Sync>(&mut self) -> Entry<T> {
308 match self.map.get_or_insert_with(|| FxHashMap::default()).entry(TypeId::of::<T>()) {
309 hash_map::Entry::Occupied(e) => {
310 Entry::Occupied(OccupiedEntry { data: e, marker: PhantomData })
311 }
312 hash_map::Entry::Vacant(e) => {
313 Entry::Vacant(VacantEntry { data: e, marker: PhantomData })
314 }
315 }
316 }
317 }
318}
319
320#[test]
321fn test_type_map() {
322 #[derive(Debug, PartialEq)]
323 struct MyType(i32);
324
325 #[derive(Debug, PartialEq, Default)]
326 struct MyType2(String);
327
328 let mut map = TypeMap::new();
329
330 map.insert(5i32);
331 map.insert(MyType(10));
332
333 assert_eq!(map.get(), Some(&5i32));
334 assert_eq!(map.get_mut(), Some(&mut 5i32));
335
336 assert_eq!(map.remove::<i32>(), Some(5i32));
337 assert!(map.get::<i32>().is_none());
338
339 assert_eq!(map.get::<bool>(), None);
340 assert_eq!(map.get(), Some(&MyType(10)));
341
342 let entry = map.entry::<MyType2>();
343
344 let mut v = entry.or_insert_with(MyType2::default);
345
346 v.0 = "Hello".into();
347
348 assert_eq!(map.get(), Some(&MyType2("Hello".into())));
349}
350