typemap_rev/
lib.rs

1//! A hashmap whose keys are defined by types.
2
3use std::any::{Any, TypeId};
4use std::collections::hash_map::IntoIter;
5use std::collections::hash_map::{
6    Entry as HashMapEntry, OccupiedEntry as HashMapOccupiedEntry, VacantEntry as HashMapVacantEntry,
7};
8use std::collections::HashMap;
9use std::fmt::Debug;
10use std::iter::FromIterator;
11use std::marker::PhantomData;
12
13/// The default type used for storing values in a [`TypeMap`].
14pub type DefaultStorage = dyn Any + Send + Sync;
15
16/// Storage type that allows cloning a [`TypeMap`] if the values inside it all
17/// implement [`Clone`].
18pub trait CloneableStorage: Any + Send + Sync {
19    #[doc(hidden)]
20    fn clone_storage(&self) -> Box<dyn CloneableStorage>;
21}
22
23impl<T: Any + Send + Sync + Clone> CloneableStorage for T {
24    fn clone_storage(&self) -> Box<dyn CloneableStorage> {
25        Box::new(self.clone())
26    }
27}
28
29impl Clone for Box<dyn CloneableStorage> {
30    fn clone(&self) -> Self {
31        (**self).clone_storage()
32    }
33}
34
35/// Storage type that allows formatting a [`TypeMap`] in debug representation if
36/// the values inside it all implement [`Debug`].
37pub trait DebuggableStorage: Any + Send + Sync + Debug {}
38impl<T: Any + Send + Sync + Debug> DebuggableStorage for T {}
39
40/// Storage type that allows cloning and formatting a [`TypeMap`] in debug representation if
41/// the values inside it all implement [`Clone`] *and* [`Debug`].
42pub trait CloneDebuggableStorage: DebuggableStorage {
43    #[doc(hidden)]
44    fn clone_storage(&self) -> Box<dyn CloneDebuggableStorage>;
45}
46
47impl<T: DebuggableStorage + Clone> CloneDebuggableStorage for T {
48    fn clone_storage(&self) -> Box<dyn CloneDebuggableStorage> {
49        Box::new(self.clone())
50    }
51}
52
53impl Clone for Box<dyn CloneDebuggableStorage> {
54    fn clone(&self) -> Self {
55        (**self).clone_storage()
56    }
57}
58
59#[doc(hidden)]
60pub trait IntoBox<T: ?Sized> {
61    fn into_box(self) -> Box<T>;
62}
63
64impl<T: Any + Send + Sync> IntoBox<(dyn Any + Send + Sync)> for T {
65    fn into_box(self) -> Box<dyn Any + Send + Sync> {
66        Box::new(self)
67    }
68}
69
70impl<T: CloneableStorage> IntoBox<dyn CloneableStorage> for T {
71    fn into_box(self) -> Box<dyn CloneableStorage> {
72        Box::new(self)
73    }
74}
75
76impl<T: DebuggableStorage> IntoBox<dyn DebuggableStorage> for T {
77    fn into_box(self) -> Box<dyn DebuggableStorage> {
78        Box::new(self)
79    }
80}
81
82impl<T: CloneDebuggableStorage> IntoBox<dyn CloneDebuggableStorage> for T {
83    fn into_box(self) -> Box<dyn CloneDebuggableStorage> {
84        Box::new(self)
85    }
86}
87
88/// TypeMapKey is used to declare key types that are eligible for use
89/// with [`TypeMap`].
90///
91/// [`TypeMap`]: struct.TypeMap.html
92pub trait TypeMapKey: Any {
93    /// Defines the value type that corresponds to this `TypeMapKey`.
94    type Value: Any + Send + Sync;
95}
96
97/// TypeMap is a simple abstraction around the standard library's [`HashMap`]
98/// type, where types are its keys. This allows for statically-checked value
99/// retrieval.
100///
101/// [`HashMap`]: std::collections::HashMap
102pub struct TypeMap<S: ?Sized = DefaultStorage>(HashMap<TypeId, Box<S>>);
103
104impl TypeMap {
105    /// Creates a new instance of `TypeMap`.
106    #[inline]
107    pub fn new() -> Self {
108        Self::custom()
109    }
110}
111
112impl<S: ?Sized + Any + Send + Sync> TypeMap<S> {
113    /// Creates a new instance of `TypeMap` with a custom storage type.
114    #[inline]
115    pub fn custom() -> Self {
116        Self(HashMap::new())
117    }
118
119    /// Returns the amount of entries in the map.
120    #[inline]
121    pub fn len(&self) -> usize {
122        self.0.len()
123    }
124
125    /// Returns an indicator whether the map is empty (no entries).
126    #[inline]
127    pub fn is_empty(&self) -> bool {
128        self.0.is_empty()
129    }
130
131    /// Clears all entries in the map.
132    #[inline]
133    pub fn clear(&mut self) {
134        self.0.clear();
135    }
136
137    /// Returns `true` if the map contains a value for the specified [`TypeMapKey`].
138    ///
139    /// ```rust
140    /// use typemap_rev::{TypeMap, TypeMapKey};
141    ///
142    /// struct Number;
143    ///
144    /// impl TypeMapKey for Number {
145    ///     type Value = i32;
146    /// }
147    ///
148    /// let mut map = TypeMap::new();
149    /// assert!(!map.contains_key::<Number>());
150    /// map.insert::<Number>(42);
151    /// assert!(map.contains_key::<Number>());
152    /// ```
153    #[inline]
154    pub fn contains_key<T>(&self) -> bool
155    where
156        T: TypeMapKey,
157    {
158        self.0.contains_key(&TypeId::of::<T>())
159    }
160
161    /// Inserts a new value based on its [`TypeMapKey`].
162    /// If the value has been already inserted, it will be overwritten
163    /// with the new value.
164    ///
165    /// ```rust
166    /// use typemap_rev::{TypeMap, TypeMapKey};
167    ///
168    /// struct Number;
169    ///
170    /// impl TypeMapKey for Number {
171    ///     type Value = i32;
172    /// }
173    ///
174    /// let mut map = TypeMap::new();
175    /// map.insert::<Number>(42);
176    /// // Overwrite the value of `Number` with -42.
177    /// map.insert::<Number>(-42);
178    /// ```
179    ///
180    /// [`TypeMapKey`]: trait.TypeMapKey.html
181    #[inline]
182    pub fn insert<T>(&mut self, value: T::Value)
183    where
184        T: TypeMapKey,
185        T::Value: IntoBox<S>,
186    {
187        self.0.insert(TypeId::of::<T>(), value.into_box());
188    }
189
190    /// Retrieve the entry based on its [`TypeMapKey`]
191    ///
192    /// [`TypeMapKey`]: trait.TypeMapKey.html
193    #[inline]
194    pub fn entry<T>(&mut self) -> Entry<'_, T, S>
195    where
196        T: TypeMapKey,
197        T::Value: IntoBox<S>,
198    {
199        match self.0.entry(TypeId::of::<T>()) {
200            HashMapEntry::Occupied(entry) => Entry::Occupied(OccupiedEntry {
201                entry,
202                _marker: PhantomData,
203            }),
204            HashMapEntry::Vacant(entry) => Entry::Vacant(VacantEntry {
205                entry,
206                _marker: PhantomData,
207            }),
208        }
209    }
210
211    /// Retrieve a reference to a value based on its [`TypeMapKey`].
212    /// Returns `None` if it couldn't be found.
213    ///
214    /// ```rust
215    /// use typemap_rev::{TypeMap, TypeMapKey};
216    ///
217    /// struct Number;
218    ///
219    /// impl TypeMapKey for Number {
220    ///     type Value = i32;
221    /// }
222    ///
223    /// let mut map = TypeMap::new();
224    /// map.insert::<Number>(42);
225    ///
226    /// assert_eq!(*map.get::<Number>().unwrap(), 42);
227    /// ```
228    ///
229    /// [`TypeMapKey`]: trait.TypeMapKey.html
230    #[inline]
231    pub fn get<T>(&self) -> Option<&T::Value>
232    where
233        T: TypeMapKey,
234        T::Value: IntoBox<S>,
235    {
236        self.0
237            .get(&TypeId::of::<T>())
238            .map(|b| unsafe { &*((&**b) as *const _ as *const T::Value) })
239    }
240
241    /// Retrieve a mutable reference to a value based on its [`TypeMapKey`].
242    /// Returns `None` if it couldn't be found.
243    ///
244    /// ```rust
245    /// use typemap_rev::{TypeMap, TypeMapKey};
246    ///
247    /// struct Number;
248    ///
249    /// impl TypeMapKey for Number {
250    ///     type Value = i32;
251    /// }
252    ///
253    /// let mut map = TypeMap::new();
254    /// map.insert::<Number>(42);
255    ///
256    /// assert_eq!(*map.get::<Number>().unwrap(), 42);
257    /// *map.get_mut::<Number>().unwrap() -= 42;
258    /// assert_eq!(*map.get::<Number>().unwrap(), 0);
259    /// ```
260    ///
261    /// [`TypeMapKey`]: trait.TypeMapKey.html
262    #[inline]
263    pub fn get_mut<T>(&mut self) -> Option<&mut T::Value>
264    where
265        T: TypeMapKey,
266        T::Value: IntoBox<S>,
267    {
268        self.0
269            .get_mut(&TypeId::of::<T>())
270            .map(|b| unsafe { &mut *((&mut **b) as *mut _ as *mut T::Value) })
271    }
272
273    /// Removes a value from the map based on its [`TypeMapKey`].
274    ///
275    /// Returns a boolean indicating whether the value existed prior to its removal.
276    ///
277    /// ```rust
278    /// use typemap_rev::{TypeMap, TypeMapKey};
279    ///
280    /// struct Text;
281    ///
282    /// impl TypeMapKey for Text {
283    ///     type Value = String;
284    /// }
285    ///
286    /// let mut map = TypeMap::new();
287    /// map.insert::<Text>(String::from("Hello TypeMap!"));
288    /// assert!(map.remove::<Text>().is_some());
289    /// assert!(map.get::<Text>().is_none());
290    /// ```
291    #[inline]
292    pub fn remove<T>(&mut self) -> Option<T::Value>
293    where
294        T: TypeMapKey,
295        T::Value: IntoBox<S>,
296    {
297        self.0
298            .remove(&TypeId::of::<T>())
299            .map(|b| *unsafe { Box::from_raw(Box::into_raw(b) as *mut T::Value) })
300    }
301}
302
303impl<S: ?Sized> Default for TypeMap<S> {
304    fn default() -> Self {
305        Self(HashMap::default())
306    }
307}
308
309impl<S: ?Sized + DebuggableStorage> Debug for TypeMap<S> {
310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311        Debug::fmt(&self.0, f)
312    }
313}
314
315impl<S: ?Sized> Clone for TypeMap<S>
316where
317    Box<S>: Clone,
318{
319    fn clone(&self) -> Self {
320        Self(self.0.clone())
321    }
322}
323
324impl<S: ?Sized> Extend<(TypeId, Box<S>)> for TypeMap<S> {
325    fn extend<T: IntoIterator<Item = (TypeId, Box<S>)>>(&mut self, iter: T) {
326        self.0.extend(iter)
327    }
328}
329
330impl<S: ?Sized> IntoIterator for TypeMap<S> {
331    type Item = (TypeId, Box<S>);
332    type IntoIter = IntoIter<TypeId, Box<S>>;
333
334    fn into_iter(self) -> Self::IntoIter {
335        self.0.into_iter()
336    }
337}
338
339impl<S: ?Sized> FromIterator<(TypeId, Box<S>)> for TypeMap<S> {
340    fn from_iter<T: IntoIterator<Item = (TypeId, Box<S>)>>(iter: T) -> Self {
341        Self(HashMap::from_iter(iter))
342    }
343}
344
345/// A view into a single entry in the [`TypeMap`],
346/// which may either be vacant or occupied.
347///
348/// This heavily mirrors the official [`Entry`] API in the standard library,
349/// but not all of it is provided due to implementation restrictions. Please
350/// refer to its documentations.
351///
352/// [`TypeMap`]: struct.TypeMap.html
353/// [`Entry`]: std::collections::hash_map::Entry
354pub enum Entry<'a, K, S: ?Sized = DefaultStorage>
355where
356    K: TypeMapKey,
357{
358    Occupied(OccupiedEntry<'a, K, S>),
359    Vacant(VacantEntry<'a, K, S>),
360}
361
362impl<'a, K, S> Entry<'a, K, S>
363where
364    K: TypeMapKey,
365    K::Value: IntoBox<S>,
366    S: ?Sized + Any + Send + Sync,
367{
368    #[inline]
369    pub fn or_insert(self, value: K::Value) -> &'a mut K::Value {
370        match self {
371            Entry::Occupied(entry) => entry.into_mut(),
372            Entry::Vacant(entry) => entry.insert(value),
373        }
374    }
375
376    #[inline]
377    pub fn or_insert_with<F>(self, f: F) -> &'a mut K::Value
378    where
379        F: FnOnce() -> K::Value,
380    {
381        match self {
382            Entry::Occupied(entry) => entry.into_mut(),
383            Entry::Vacant(entry) => entry.insert(f()),
384        }
385    }
386
387    #[inline]
388    pub fn and_modify<F>(self, f: F) -> Self
389    where
390        F: FnOnce(&mut K::Value),
391    {
392        match self {
393            Entry::Occupied(mut entry) => {
394                f(entry.get_mut());
395                Entry::Occupied(entry)
396            }
397            Entry::Vacant(entry) => Entry::Vacant(entry),
398        }
399    }
400}
401
402impl<'a, K, S> Entry<'a, K, S>
403where
404    K: TypeMapKey,
405    K::Value: Default + IntoBox<S>,
406    S: ?Sized + Any + Send + Sync,
407{
408    #[inline]
409    pub fn or_default(self) -> &'a mut K::Value {
410        self.or_insert_with(<K::Value as Default>::default)
411    }
412}
413
414pub struct OccupiedEntry<'a, K, S: ?Sized = DefaultStorage>
415where
416    K: TypeMapKey,
417{
418    entry: HashMapOccupiedEntry<'a, TypeId, Box<S>>,
419    _marker: PhantomData<&'a K::Value>,
420}
421
422impl<'a, K, S> OccupiedEntry<'a, K, S>
423where
424    K: TypeMapKey,
425    K::Value: IntoBox<S>,
426    S: ?Sized + Any + Send + Sync,
427{
428    #[inline]
429    pub fn get(&self) -> &K::Value {
430        unsafe { &*((&**self.entry.get()) as *const _ as *const K::Value) }
431    }
432
433    #[inline]
434    pub fn get_mut(&mut self) -> &mut K::Value {
435        unsafe { &mut *((&mut **self.entry.get_mut()) as *mut _ as *mut K::Value) }
436    }
437
438    #[inline]
439    pub fn into_mut(self) -> &'a mut K::Value {
440        unsafe { &mut *((&mut **self.entry.into_mut()) as *mut _ as *mut K::Value) }
441    }
442
443    #[inline]
444    pub fn insert(&mut self, value: K::Value) {
445        self.entry.insert(value.into_box());
446    }
447
448    #[inline]
449    pub fn remove(self) {
450        self.entry.remove();
451    }
452}
453
454pub struct VacantEntry<'a, K, S: ?Sized = DefaultStorage>
455where
456    K: TypeMapKey,
457{
458    entry: HashMapVacantEntry<'a, TypeId, Box<S>>,
459    _marker: PhantomData<&'a K::Value>,
460}
461
462impl<'a, K, S> VacantEntry<'a, K, S>
463where
464    K: TypeMapKey,
465    K::Value: IntoBox<S>,
466    S: ?Sized + Any + Send + Sync,
467{
468    #[inline]
469    pub fn insert(self, value: K::Value) -> &'a mut K::Value {
470        let value = self.entry.insert(value.into_box());
471        unsafe { &mut *((&mut **value) as *mut _ as *mut K::Value) }
472    }
473}
474
475#[cfg(test)]
476mod test {
477    use super::*;
478
479    struct Counter;
480
481    impl TypeMapKey for Counter {
482        type Value = u64;
483    }
484
485    #[test]
486    fn typemap_counter() {
487        let mut map = TypeMap::new();
488
489        assert_eq!(map.len(), 0);
490        assert!(map.is_empty());
491
492        map.insert::<Counter>(0);
493        assert_eq!(map.len(), 1);
494        assert!(!map.is_empty());
495
496        assert_eq!(*map.get::<Counter>().unwrap(), 0);
497
498        for _ in 0..100 {
499            *map.get_mut::<Counter>().unwrap() += 1;
500        }
501
502        assert_eq!(*map.get::<Counter>().unwrap(), 100);
503
504        map.clear();
505        assert!(map.get::<Counter>().is_none());
506        assert_eq!(map.len(), 0);
507        assert!(map.is_empty());
508    }
509
510    #[test]
511    fn typemap_entry() {
512        let mut map = TypeMap::new();
513
514        assert_eq!(map.get::<Counter>(), None);
515        *map.entry::<Counter>().or_insert(0) += 42;
516        assert_eq!(*map.get::<Counter>().unwrap(), 42);
517    }
518
519    struct Text;
520
521    impl TypeMapKey for Text {
522        type Value = String;
523    }
524
525    #[test]
526    fn typemap_remove() {
527        let mut map = TypeMap::new();
528
529        map.insert::<Text>(String::from("foobar"));
530
531        // This will give a &String
532        assert_eq!(map.get::<Text>().unwrap(), "foobar");
533
534        // Ensure we get an owned String back.
535        let original: String = map.remove::<Text>().unwrap();
536        assert_eq!(original, "foobar");
537
538        // Ensure our String is really gone from the map.
539        assert!(map.get::<Text>().is_none());
540    }
541
542    #[test]
543    fn typemap_default() {
544        fn ensure_default<T: Default>() {}
545
546        ensure_default::<TypeMap>();
547
548        let map = TypeMap::<DefaultStorage>::default();
549        assert!(map.get::<Text>().is_none());
550    }
551
552    #[test]
553    fn typemap_iter() {
554        let mut map = TypeMap::new();
555        map.insert::<Text>(String::from("foobar"));
556
557        // creating the iterator
558        let mut iterator = map.into_iter();
559
560        // ensuring that the iterator contains our entries
561        assert_eq!(iterator.next().unwrap().0, TypeId::of::<Text>());
562    }
563
564    #[test]
565    fn typemap_extend() {
566        let mut map = TypeMap::new();
567        map.insert::<Text>(String::from("foobar"));
568
569        let mut map_2 = TypeMap::new();
570        // extending our second map with the first one
571        map_2.extend(map);
572
573        // ensuring that the new map now contains the entries from the first one
574        let original = map_2.get::<Text>().unwrap();
575        assert_eq!(original, "foobar");
576    }
577
578    fn is_debug<T: Debug>() {}
579    fn is_clone<T: Clone>() {}
580
581    #[test]
582    fn typemap_debug() {
583        is_debug::<i32>();
584        is_debug::<TypeMap<dyn DebuggableStorage>>();
585        is_debug::<TypeMap<dyn CloneDebuggableStorage>>();
586    }
587
588    #[test]
589    fn typemap_clone() {
590        is_clone::<i32>();
591        is_clone::<TypeMap<dyn CloneableStorage>>();
592        is_clone::<TypeMap<dyn CloneDebuggableStorage>>();
593
594        let mut map = TypeMap::<dyn CloneableStorage>::custom();
595        map.insert::<Text>(String::from("foo"));
596
597        let map_2 = map.clone();
598        assert_eq!(*map_2.get::<Text>().unwrap(), "foo");
599
600        let mut map = TypeMap::<dyn CloneDebuggableStorage>::custom();
601        map.insert::<Text>(String::from("foo"));
602
603        let map_2 = map.clone();
604        assert_eq!(*map_2.get::<Text>().unwrap(), "foo");
605    }
606}