Skip to main content

bitwarden_state/
any_map.rs

1use std::{
2    any::{Any, TypeId},
3    collections::HashMap,
4    sync::RwLock,
5};
6
7/// A concurrent map holding type-erased values, each keyed by its own type.
8pub(crate) struct AnyMap {
9    inner: RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>,
10}
11
12impl AnyMap {
13    /// Creates an empty map.
14    pub(crate) fn new() -> Self {
15        Self {
16            inner: RwLock::new(HashMap::new()),
17        }
18    }
19
20    /// Inserts a value under its own type, replacing any existing value of that type.
21    ///
22    /// The key is the static type of `value` at the call site, so any coercion has to happen
23    /// before inserting: `Arc<ConcreteRepo>` and `Arc<dyn Repository<T>>` are different keys.
24    pub(crate) fn insert<V: Send + Sync + 'static>(&self, value: V) {
25        self.inner
26            .write()
27            .expect("RwLock should not be poisoned")
28            .insert(TypeId::of::<V>(), Box::new(value));
29    }
30
31    /// Retrieves a clone of the value of type `V`, or `None` if none was inserted.
32    pub(crate) fn get<V: Clone + Send + Sync + 'static>(&self) -> Option<V> {
33        self.inner
34            .read()
35            .expect("RwLock should not be poisoned")
36            .get(&TypeId::of::<V>())
37            .and_then(|value| value.downcast_ref::<V>())
38            .cloned()
39    }
40
41    /// Removes all entries.
42    pub(crate) fn clear(&self) {
43        self.inner
44            .write()
45            .expect("RwLock should not be poisoned")
46            .clear();
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_roundtrip() {
56        let map = AnyMap::new();
57
58        map.insert(42_u32);
59        map.insert("hello".to_string());
60
61        assert_eq!(map.get::<u32>(), Some(42));
62        assert_eq!(map.get::<String>(), Some("hello".to_string()));
63    }
64
65    #[test]
66    fn test_get_missing_type_returns_none() {
67        let map = AnyMap::new();
68        map.insert(42_u32);
69
70        assert_eq!(map.get::<String>(), None);
71        assert_eq!(map.get::<u8>(), None);
72    }
73
74    #[test]
75    fn test_insert_overwrites() {
76        let map = AnyMap::new();
77
78        map.insert(1_u32);
79        map.insert(2_u32);
80
81        assert_eq!(map.get::<u32>(), Some(2));
82    }
83
84    #[test]
85    fn test_clear() {
86        let map = AnyMap::new();
87        map.insert(1_u32);
88        map.insert("a".to_string());
89
90        map.clear();
91
92        assert_eq!(map.get::<u32>(), None);
93        assert_eq!(map.get::<String>(), None);
94    }
95}