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