type_map/
lib.rs

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