1use std::collections::{BTreeSet, HashMap};
17
18use lru::LruCache;
19use mdk_storage_traits::GroupId;
20use mdk_storage_traits::groups::types::{Group, GroupExporterSecret, GroupRelay};
21use mdk_storage_traits::messages::types::{Message, ProcessedMessage};
22use mdk_storage_traits::welcomes::types::{ProcessedWelcome, Welcome};
23use nostr::EventId;
24
25use crate::mls_storage::GroupDataType;
26
27#[derive(Clone)]
56pub struct GroupScopedSnapshot {
57 pub(crate) group_id: GroupId,
59
60 pub(crate) created_at: u64,
62
63 pub(crate) mls_group_data: HashMap<(Vec<u8>, GroupDataType), Vec<u8>>,
66 pub(crate) mls_own_leaf_nodes: Vec<Vec<u8>>,
68 pub(crate) mls_proposals: HashMap<Vec<u8>, Vec<u8>>,
70 pub(crate) mls_epoch_key_pairs: HashMap<(Vec<u8>, u32), Vec<u8>>,
72
73 pub(crate) group: Option<Group>,
76 pub(crate) group_relays: BTreeSet<GroupRelay>,
78 pub(crate) group_exporter_secrets: HashMap<u64, GroupExporterSecret>,
80 pub(crate) group_mip04_exporter_secrets: HashMap<u64, GroupExporterSecret>,
82}
83
84#[derive(Clone)]
116pub struct MemoryStorageSnapshot {
117 pub(crate) mls_group_data: HashMap<(Vec<u8>, GroupDataType), Vec<u8>>,
119 pub(crate) mls_own_leaf_nodes: HashMap<Vec<u8>, Vec<Vec<u8>>>,
120 pub(crate) mls_proposals: HashMap<(Vec<u8>, Vec<u8>), Vec<u8>>,
121 pub(crate) mls_key_packages: HashMap<Vec<u8>, Vec<u8>>,
122 pub(crate) mls_psks: HashMap<Vec<u8>, Vec<u8>>,
123 pub(crate) mls_signature_keys: HashMap<Vec<u8>, Vec<u8>>,
124 pub(crate) mls_encryption_keys: HashMap<Vec<u8>, Vec<u8>>,
125 pub(crate) mls_epoch_key_pairs: HashMap<(Vec<u8>, Vec<u8>, u32), Vec<u8>>,
126
127 pub(crate) groups: HashMap<GroupId, Group>,
129 pub(crate) groups_by_nostr_id: HashMap<[u8; 32], Group>,
130 pub(crate) group_relays: HashMap<GroupId, BTreeSet<GroupRelay>>,
131 pub(crate) group_exporter_secrets: HashMap<(GroupId, u64), GroupExporterSecret>,
132 pub(crate) group_mip04_exporter_secrets: HashMap<(GroupId, u64), GroupExporterSecret>,
133 pub(crate) welcomes: HashMap<EventId, Welcome>,
134 pub(crate) processed_welcomes: HashMap<EventId, ProcessedWelcome>,
135 pub(crate) messages: HashMap<EventId, Message>,
136 pub(crate) messages_by_group: HashMap<GroupId, HashMap<EventId, Message>>,
137 pub(crate) processed_messages: HashMap<EventId, ProcessedMessage>,
138}
139
140#[cfg(test)]
141impl MemoryStorageSnapshot {
142 pub(crate) fn new() -> Self {
144 Self {
145 mls_group_data: HashMap::new(),
146 mls_own_leaf_nodes: HashMap::new(),
147 mls_proposals: HashMap::new(),
148 mls_key_packages: HashMap::new(),
149 mls_psks: HashMap::new(),
150 mls_signature_keys: HashMap::new(),
151 mls_encryption_keys: HashMap::new(),
152 mls_epoch_key_pairs: HashMap::new(),
153 groups: HashMap::new(),
154 groups_by_nostr_id: HashMap::new(),
155 group_relays: HashMap::new(),
156 group_exporter_secrets: HashMap::new(),
157 group_mip04_exporter_secrets: HashMap::new(),
158 welcomes: HashMap::new(),
159 processed_welcomes: HashMap::new(),
160 messages: HashMap::new(),
161 messages_by_group: HashMap::new(),
162 processed_messages: HashMap::new(),
163 }
164 }
165}
166
167pub(crate) trait LruCacheExt<K, V> {
169 fn clone_to_hashmap(&self) -> HashMap<K, V>
171 where
172 K: Clone + std::hash::Hash + Eq,
173 V: Clone;
174}
175
176impl<K, V> LruCacheExt<K, V> for LruCache<K, V> {
177 fn clone_to_hashmap(&self) -> HashMap<K, V>
178 where
179 K: Clone + std::hash::Hash + Eq,
180 V: Clone,
181 {
182 self.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
183 }
184}
185
186pub(crate) trait HashMapToLruExt<K, V> {
188 fn restore_to_lru(&self, cache: &mut LruCache<K, V>)
190 where
191 K: Clone + std::hash::Hash + Eq,
192 V: Clone;
193}
194
195impl<K, V> HashMapToLruExt<K, V> for HashMap<K, V> {
196 fn restore_to_lru(&self, cache: &mut LruCache<K, V>)
197 where
198 K: Clone + std::hash::Hash + Eq,
199 V: Clone,
200 {
201 cache.clear();
202 for (k, v) in self.iter() {
203 cache.put(k.clone(), v.clone());
204 }
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use std::num::NonZeroUsize;
211
212 use super::*;
213
214 #[test]
215 fn test_lru_cache_clone_to_hashmap() {
216 let mut cache: LruCache<String, i32> = LruCache::new(NonZeroUsize::new(10).unwrap());
217 cache.put("a".to_string(), 1);
218 cache.put("b".to_string(), 2);
219 cache.put("c".to_string(), 3);
220
221 let map = cache.clone_to_hashmap();
222 assert_eq!(map.len(), 3);
223 assert_eq!(map.get("a"), Some(&1));
224 assert_eq!(map.get("b"), Some(&2));
225 assert_eq!(map.get("c"), Some(&3));
226 }
227
228 #[test]
229 fn test_hashmap_restore_to_lru() {
230 let mut map = HashMap::new();
231 map.insert("x".to_string(), 10);
232 map.insert("y".to_string(), 20);
233
234 let mut cache: LruCache<String, i32> = LruCache::new(NonZeroUsize::new(10).unwrap());
235 cache.put("old".to_string(), 999);
236
237 map.restore_to_lru(&mut cache);
238
239 assert_eq!(cache.len(), 2);
240 assert_eq!(cache.get(&"x".to_string()), Some(&10));
241 assert_eq!(cache.get(&"y".to_string()), Some(&20));
242 assert!(cache.get(&"old".to_string()).is_none());
243 }
244
245 #[test]
246 fn test_empty_snapshot() {
247 let snapshot = MemoryStorageSnapshot::new();
248 assert!(snapshot.mls_group_data.is_empty());
249 assert!(snapshot.groups.is_empty());
250 assert!(snapshot.messages.is_empty());
251 }
252}