Skip to main content

opentele2_browser/
map.rs

1//! A small insertion-ordered string map, the shape of browserforge's dicts.
2
3/// A map from node names to `V` that iterates in insertion order.
4///
5/// Inserting an existing key replaces its value in place, as a Python dict
6/// does. Lookups are linear: the maps hold one entry per network node, a few
7/// dozen at most, where a scan beats hashing.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct OrderedMap<V> {
10    entries: Vec<(String, V)>,
11}
12
13/// Node name to value, in insertion order: a (partial) network sample.
14pub type Values = OrderedMap<String>;
15
16/// Node name to the list of values it may take, in insertion order.
17pub type Restrictions = OrderedMap<Vec<String>>;
18
19impl<V> Default for OrderedMap<V> {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl<V> OrderedMap<V> {
26    /// Creates an empty map.
27    #[must_use]
28    pub const fn new() -> Self {
29        Self {
30            entries: Vec::new(),
31        }
32    }
33
34    /// Returns the number of entries.
35    #[must_use]
36    pub fn len(&self) -> usize {
37        self.entries.len()
38    }
39
40    /// Returns `true` when the map holds no entry.
41    #[must_use]
42    pub fn is_empty(&self) -> bool {
43        self.entries.is_empty()
44    }
45
46    fn position(&self, key: &str) -> Option<usize> {
47        self.entries.iter().position(|(k, _)| k == key)
48    }
49
50    /// Returns the value stored under `key`.
51    #[must_use]
52    pub fn get(&self, key: &str) -> Option<&V> {
53        self.position(key).map(|i| &self.entries[i].1)
54    }
55
56    /// Returns a mutable reference to the value stored under `key`.
57    pub fn get_mut(&mut self, key: &str) -> Option<&mut V> {
58        self.position(key).map(|i| &mut self.entries[i].1)
59    }
60
61    /// Returns `true` when `key` is present.
62    #[must_use]
63    pub fn contains_key(&self, key: &str) -> bool {
64        self.position(key).is_some()
65    }
66
67    /// Inserts `value` under `key`, returning the value it replaced. A new key
68    /// goes last; an existing key keeps its position.
69    pub fn insert(&mut self, key: impl Into<String>, value: V) -> Option<V> {
70        let key = key.into();
71        if let Some(i) = self.position(&key) {
72            return Some(std::mem::replace(&mut self.entries[i].1, value));
73        }
74        self.entries.push((key, value));
75        None
76    }
77
78    /// Removes `key`, keeping the order of the remaining entries.
79    pub fn remove(&mut self, key: &str) -> Option<V> {
80        self.position(key).map(|i| self.entries.remove(i).1)
81    }
82
83    /// Iterates over the entries in insertion order.
84    pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &V)> {
85        self.entries.iter().map(|(k, v)| (k.as_str(), v))
86    }
87
88    /// Iterates over the keys in insertion order.
89    pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
90        self.entries.iter().map(|(k, _)| k.as_str())
91    }
92}
93
94impl<K: Into<String>, V> FromIterator<(K, V)> for OrderedMap<V> {
95    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
96        let mut map = Self::new();
97        map.extend(iter);
98        map
99    }
100}
101
102impl<K: Into<String>, V> Extend<(K, V)> for OrderedMap<V> {
103    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
104        for (k, v) in iter {
105            self.insert(k, v);
106        }
107    }
108}
109
110impl<V> IntoIterator for OrderedMap<V> {
111    type Item = (String, V);
112    type IntoIter = std::vec::IntoIter<(String, V)>;
113
114    fn into_iter(self) -> Self::IntoIter {
115        self.entries.into_iter()
116    }
117}
118
119impl<'a, V> IntoIterator for &'a OrderedMap<V> {
120    type Item = (&'a str, &'a V);
121    type IntoIter =
122        std::iter::Map<std::slice::Iter<'a, (String, V)>, fn(&'a (String, V)) -> (&'a str, &'a V)>;
123
124    fn into_iter(self) -> Self::IntoIter {
125        fn split<V>(entry: &(String, V)) -> (&str, &V) {
126            (entry.0.as_str(), &entry.1)
127        }
128        self.entries.iter().map(split)
129    }
130}