|
14 | 14 |
|
15 | 15 | //! An [`ObservableMap`] implementation. |
16 | 16 |
|
17 | | -#[cfg(not(target_arch = "wasm32"))] |
18 | | -mod impl_non_wasm32 { |
19 | | - use std::{borrow::Borrow, collections::HashMap, hash::Hash}; |
20 | | - |
21 | | - use eyeball_im::{ObservableVector, Vector, VectorDiff}; |
22 | | - use futures_util::Stream; |
23 | | - |
24 | | - /// An observable map. |
25 | | - /// |
26 | | - /// This is an “observable map” naive implementation. Just like regular |
27 | | - /// hashmap, we have a redirection from a key to a position, and from a |
28 | | - /// position to a value. The (key, position) tuples are stored in an |
29 | | - /// [`HashMap`]. The (position, value) tuples are stored in an |
30 | | - /// [`ObservableVector`]. The (key, position) tuple is only provided for |
31 | | - /// fast _reading_ implementations, like `Self::get` and |
32 | | - /// `Self::get_or_create`. The (position, value) tuples are observable, |
33 | | - /// this is what interests us the most here. |
34 | | - /// |
35 | | - /// Why not implementing a new `ObservableMap` type in `eyeball-im` instead |
36 | | - /// of this custom implementation? Because we want to continue providing |
37 | | - /// `VectorDiff` when observing the changes, so that the rest of the API in |
38 | | - /// the Matrix Rust SDK aren't broken. Indeed, an `ObservableMap` must |
39 | | - /// produce `MapDiff`, which would be quite different. |
40 | | - /// Plus, we would like to re-use all our existing code, test, stream |
41 | | - /// adapters and so on. |
42 | | - /// |
43 | | - /// This is a trade-off. This implementation is simple enough for the |
44 | | - /// moment, and basically does the job. |
45 | | - #[derive(Debug)] |
46 | | - pub(crate) struct ObservableMap<K, V> |
47 | | - where |
48 | | - V: Clone + Send + Sync + 'static, |
49 | | - { |
50 | | - /// The (key, position) tuples. |
51 | | - mapping: HashMap<K, usize>, |
| 17 | +use std::{borrow::Borrow, collections::HashMap, hash::Hash}; |
| 18 | + |
| 19 | +use eyeball_im::{ObservableVector, Vector, VectorDiff}; |
| 20 | +use futures_util::Stream; |
| 21 | + |
| 22 | +/// An observable map. |
| 23 | +/// |
| 24 | +/// This is an “observable map” naive implementation. Just like regular |
| 25 | +/// hashmap, we have a redirection from a key to a position, and from a |
| 26 | +/// position to a value. The (key, position) tuples are stored in an |
| 27 | +/// [`HashMap`]. The (position, value) tuples are stored in an |
| 28 | +/// [`ObservableVector`]. The (key, position) tuple is only provided for |
| 29 | +/// fast _reading_ implementations, like `Self::get` and |
| 30 | +/// `Self::get_or_create`. The (position, value) tuples are observable, |
| 31 | +/// this is what interests us the most here. |
| 32 | +/// |
| 33 | +/// Why not implementing a new `ObservableMap` type in `eyeball-im` instead |
| 34 | +/// of this custom implementation? Because we want to continue providing |
| 35 | +/// `VectorDiff` when observing the changes, so that the rest of the API in |
| 36 | +/// the Matrix Rust SDK aren't broken. Indeed, an `ObservableMap` must |
| 37 | +/// produce `MapDiff`, which would be quite different. |
| 38 | +/// Plus, we would like to re-use all our existing code, test, stream |
| 39 | +/// adapters and so on. |
| 40 | +/// |
| 41 | +/// This is a trade-off. This implementation is simple enough for the |
| 42 | +/// moment, and basically does the job. |
| 43 | +#[derive(Debug)] |
| 44 | +pub(crate) struct ObservableMap<K, V> |
| 45 | +where |
| 46 | + V: Clone + 'static, |
| 47 | +{ |
| 48 | + /// The (key, position) tuples. |
| 49 | + mapping: HashMap<K, usize>, |
| 50 | + |
| 51 | + /// The values where the indices are the `position` part of |
| 52 | + /// `Self::mapping`. |
| 53 | + values: ObservableVector<V>, |
| 54 | +} |
52 | 55 |
|
53 | | - /// The values where the indices are the `position` part of |
54 | | - /// `Self::mapping`. |
55 | | - values: ObservableVector<V>, |
| 56 | +impl<K, V> ObservableMap<K, V> |
| 57 | +where |
| 58 | + K: Hash + Eq, |
| 59 | + V: Clone + 'static, |
| 60 | +{ |
| 61 | + /// Create a new `Self`. |
| 62 | + pub(crate) fn new() -> Self { |
| 63 | + Self { mapping: HashMap::new(), values: ObservableVector::new() } |
56 | 64 | } |
57 | 65 |
|
58 | | - impl<K, V> ObservableMap<K, V> |
59 | | - where |
60 | | - K: Hash + Eq, |
61 | | - V: Clone + Send + Sync + 'static, |
62 | | - { |
63 | | - /// Create a new `Self`. |
64 | | - pub(crate) fn new() -> Self { |
65 | | - Self { mapping: HashMap::new(), values: ObservableVector::new() } |
66 | | - } |
67 | | - |
68 | | - /// Insert a new `V` in the collection. |
69 | | - /// |
70 | | - /// If the `V` value already exists, it will be updated to the new one. |
71 | | - pub(crate) fn insert(&mut self, key: K, value: V) -> usize { |
72 | | - match self.mapping.get(&key) { |
73 | | - Some(position) => { |
74 | | - self.values.set(*position, value); |
75 | | - |
76 | | - *position |
77 | | - } |
78 | | - None => { |
79 | | - let position = self.values.len(); |
80 | | - |
81 | | - self.values.push_back(value); |
82 | | - self.mapping.insert(key, position); |
| 66 | + /// Insert a new `V` in the collection. |
| 67 | + /// |
| 68 | + /// If the `V` value already exists, it will be updated to the new one. |
| 69 | + pub(crate) fn insert(&mut self, key: K, value: V) -> usize { |
| 70 | + match self.mapping.get(&key) { |
| 71 | + Some(position) => { |
| 72 | + self.values.set(*position, value); |
83 | 73 |
|
84 | | - position |
85 | | - } |
| 74 | + *position |
86 | 75 | } |
87 | | - } |
| 76 | + None => { |
| 77 | + let position = self.values.len(); |
88 | 78 |
|
89 | | - /// Reading one `V` value based on their ID, if it exists. |
90 | | - pub(crate) fn get<L>(&self, key: &L) -> Option<&V> |
91 | | - where |
92 | | - K: Borrow<L>, |
93 | | - L: Hash + Eq + ?Sized, |
94 | | - { |
95 | | - self.mapping.get(key).and_then(|position| self.values.get(*position)) |
96 | | - } |
| 79 | + self.values.push_back(value); |
| 80 | + self.mapping.insert(key, position); |
97 | 81 |
|
98 | | - /// Reading one `V` value based on their ID, or create a new one (by |
99 | | - /// using `default`). |
100 | | - pub(crate) fn get_or_create<L, F>(&mut self, key: &L, default: F) -> &V |
101 | | - where |
102 | | - K: Borrow<L>, |
103 | | - L: Hash + Eq + ?Sized + ToOwned<Owned = K>, |
104 | | - F: FnOnce() -> V, |
105 | | - { |
106 | | - let position = match self.mapping.get(key) { |
107 | | - Some(position) => *position, |
108 | | - None => { |
109 | | - let value = default(); |
110 | | - let position = self.values.len(); |
111 | | - |
112 | | - self.values.push_back(value); |
113 | | - self.mapping.insert(key.to_owned(), position); |
114 | | - |
115 | | - position |
116 | | - } |
117 | | - }; |
118 | | - |
119 | | - self.values |
120 | | - .get(position) |
121 | | - .expect("Value should be present or has just been inserted, but it's missing") |
122 | | - } |
123 | | - |
124 | | - /// Return an iterator over the existing values. |
125 | | - pub(crate) fn iter(&self) -> impl Iterator<Item = &V> { |
126 | | - self.values.iter() |
127 | | - } |
128 | | - |
129 | | - /// Get a [`Stream`] of the values. |
130 | | - pub(crate) fn stream(&self) -> (Vector<V>, impl Stream<Item = Vec<VectorDiff<V>>>) { |
131 | | - self.values.subscribe().into_values_and_batched_stream() |
132 | | - } |
133 | | - |
134 | | - /// Remove a `V` value based on their ID, if it exists. |
135 | | - /// |
136 | | - /// Returns the removed value. |
137 | | - pub(crate) fn remove<L>(&mut self, key: &L) -> Option<V> |
138 | | - where |
139 | | - K: Borrow<L>, |
140 | | - L: Hash + Eq + ?Sized, |
141 | | - { |
142 | | - let position = self.mapping.remove(key)?; |
143 | | - Some(self.values.remove(position)) |
| 82 | + position |
| 83 | + } |
144 | 84 | } |
145 | 85 | } |
146 | | -} |
147 | 86 |
|
148 | | -#[cfg(target_arch = "wasm32")] |
149 | | -mod impl_wasm32 { |
150 | | - use std::{borrow::Borrow, collections::BTreeMap, hash::Hash}; |
151 | | - |
152 | | - /// An observable map for Wasm. It's a simple wrapper around `BTreeMap`. |
153 | | - #[derive(Debug)] |
154 | | - pub(crate) struct ObservableMap<K, V>(BTreeMap<K, V>) |
| 87 | + /// Reading one `V` value based on their ID, if it exists. |
| 88 | + pub(crate) fn get<L>(&self, key: &L) -> Option<&V> |
155 | 89 | where |
156 | | - V: Clone + 'static; |
| 90 | + K: Borrow<L>, |
| 91 | + L: Hash + Eq + ?Sized, |
| 92 | + { |
| 93 | + self.mapping.get(key).and_then(|position| self.values.get(*position)) |
| 94 | + } |
157 | 95 |
|
158 | | - impl<K, V> ObservableMap<K, V> |
| 96 | + /// Reading one `V` value based on their ID, or create a new one (by |
| 97 | + /// using `default`). |
| 98 | + pub(crate) fn get_or_create<L, F>(&mut self, key: &L, default: F) -> &V |
159 | 99 | where |
160 | | - K: Hash + Eq + Ord, |
161 | | - V: Clone + 'static, |
| 100 | + K: Borrow<L>, |
| 101 | + L: Hash + Eq + ?Sized + ToOwned<Owned = K>, |
| 102 | + F: FnOnce() -> V, |
162 | 103 | { |
163 | | - /// Create a new `Self`. |
164 | | - pub(crate) fn new() -> Self { |
165 | | - Self(BTreeMap::new()) |
166 | | - } |
| 104 | + let position = match self.mapping.get(key) { |
| 105 | + Some(position) => *position, |
| 106 | + None => { |
| 107 | + let value = default(); |
| 108 | + let position = self.values.len(); |
167 | 109 |
|
168 | | - /// Insert a new `V` in the collection. |
169 | | - /// |
170 | | - /// If the `V` value already exists, it will be updated to the new one. |
171 | | - pub(crate) fn insert(&mut self, key: K, value: V) { |
172 | | - self.0.insert(key, value); |
173 | | - } |
| 110 | + self.values.push_back(value); |
| 111 | + self.mapping.insert(key.to_owned(), position); |
174 | 112 |
|
175 | | - /// Reading one `V` value based on their ID, if it exists. |
176 | | - pub(crate) fn get<L>(&self, key: &L) -> Option<&V> |
177 | | - where |
178 | | - K: Borrow<L>, |
179 | | - L: Hash + Eq + Ord + ?Sized, |
180 | | - { |
181 | | - self.0.get(key) |
182 | | - } |
| 113 | + position |
| 114 | + } |
| 115 | + }; |
183 | 116 |
|
184 | | - /// Reading one `V` value based on their ID, or create a new one (by |
185 | | - /// using `default`). |
186 | | - pub(crate) fn get_or_create<L, F>(&mut self, key: &L, default: F) -> &V |
187 | | - where |
188 | | - K: Borrow<L>, |
189 | | - L: Hash + Eq + ?Sized + ToOwned<Owned = K>, |
190 | | - F: FnOnce() -> V, |
191 | | - { |
192 | | - self.0.entry(key.to_owned()).or_insert_with(default) |
193 | | - } |
| 117 | + self.values |
| 118 | + .get(position) |
| 119 | + .expect("Value should be present or has just been inserted, but it's missing") |
| 120 | + } |
194 | 121 |
|
195 | | - /// Return an iterator over the existing values. |
196 | | - pub(crate) fn iter(&self) -> impl Iterator<Item = &V> { |
197 | | - self.0.values() |
198 | | - } |
| 122 | + /// Return an iterator over the existing values. |
| 123 | + pub(crate) fn iter(&self) -> impl Iterator<Item = &V> { |
| 124 | + self.values.iter() |
| 125 | + } |
199 | 126 |
|
200 | | - /// Remove a `V` value based on their ID, if it exists. |
201 | | - /// |
202 | | - /// Returns the removed value. |
203 | | - pub(crate) fn remove<L>(&mut self, key: &L) -> Option<V> |
204 | | - where |
205 | | - K: Borrow<L>, |
206 | | - L: Hash + Eq + Ord + ?Sized, |
207 | | - { |
208 | | - self.0.remove(key) |
209 | | - } |
| 127 | + /// Get a [`Stream`] of the values. |
| 128 | + pub(crate) fn stream(&self) -> (Vector<V>, impl Stream<Item = Vec<VectorDiff<V>>>) { |
| 129 | + self.values.subscribe().into_values_and_batched_stream() |
210 | 130 | } |
211 | | -} |
212 | 131 |
|
213 | | -#[cfg(not(target_arch = "wasm32"))] |
214 | | -pub(crate) use impl_non_wasm32::ObservableMap; |
215 | | -#[cfg(target_arch = "wasm32")] |
216 | | -pub(crate) use impl_wasm32::ObservableMap; |
| 132 | + /// Remove a `V` value based on their ID, if it exists. |
| 133 | + /// |
| 134 | + /// Returns the removed value. |
| 135 | + pub(crate) fn remove<L>(&mut self, key: &L) -> Option<V> |
| 136 | + where |
| 137 | + K: Borrow<L>, |
| 138 | + L: Hash + Eq + ?Sized, |
| 139 | + { |
| 140 | + let position = self.mapping.remove(key)?; |
| 141 | + Some(self.values.remove(position)) |
| 142 | + } |
| 143 | +} |
217 | 144 |
|
218 | 145 | #[cfg(test)] |
219 | 146 | mod tests { |
220 | | - #[cfg(not(target_arch = "wasm32"))] |
221 | 147 | use eyeball_im::VectorDiff; |
222 | | - #[cfg(not(target_arch = "wasm32"))] |
223 | 148 | use stream_assert::{assert_closed, assert_next_eq, assert_pending}; |
224 | 149 |
|
225 | 150 | use super::ObservableMap; |
@@ -314,7 +239,6 @@ mod tests { |
314 | 239 | ); |
315 | 240 | } |
316 | 241 |
|
317 | | - #[cfg(not(target_arch = "wasm32"))] |
318 | 242 | #[test] |
319 | 243 | fn test_stream() { |
320 | 244 | let mut map = ObservableMap::<char, char>::new(); |
|
0 commit comments