1use std::sync::Arc;
2
3use bitwarden_error::bitwarden_error;
4use thiserror::Error;
5
6use crate::{
7 any_map::AnyMap,
8 persisted_value::{PersistedValue, PersistedValueKey},
9 repository::{Repository, RepositoryItem, RepositoryMigrations},
10 sdk_managed::{Database, DatabaseConfiguration, DatabaseError, MemoryDatabase, SystemDatabase},
11};
12
13pub struct StateRegistry {
16 database: SystemDatabase,
17 client_managed: AnyMap,
18 #[cfg(feature = "debug-capabilities")]
22 pub(crate) debug: crate::debug::DebugRegistry,
23}
24
25impl std::fmt::Debug for StateRegistry {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_struct("StateRegistry").finish()
28 }
29}
30
31#[allow(missing_docs)]
32#[bitwarden_error(flat)]
33#[derive(Debug, Error)]
34pub enum StateRegistryError {
35 #[error("Database is not initialized")]
36 DatabaseNotInitialized,
37
38 #[error(transparent)]
39 Database(#[from] DatabaseError),
40}
41
42impl StateRegistry {
43 pub fn new_with_memory_db() -> Self {
45 StateRegistry {
46 database: SystemDatabase::Memory(MemoryDatabase::new()),
47 client_managed: AnyMap::new(),
48 #[cfg(feature = "debug-capabilities")]
49 debug: crate::debug::DebugRegistry::new(),
50 }
51 }
52
53 pub async fn new_with_db(
55 configuration: DatabaseConfiguration,
56 migrations: RepositoryMigrations,
57 ) -> Result<Self, DatabaseError> {
58 let database = SystemDatabase::initialize(configuration, migrations.clone()).await?;
59 let registry = StateRegistry {
60 database,
61 client_managed: AnyMap::new(),
62 #[cfg(feature = "debug-capabilities")]
63 debug: crate::debug::DebugRegistry::new(),
64 };
65 #[cfg(feature = "debug-capabilities")]
67 registry.debug.register_migrations(&migrations);
68 Ok(registry)
69 }
70
71 pub fn value<K: PersistedValueKey>(&self) -> PersistedValue<K> {
73 PersistedValue::new(self.database.get_persisted_value::<K::Value>(K::NAME))
74 }
75
76 pub fn register_client_managed<T: RepositoryItem>(&self, value: Arc<dyn Repository<T>>) {
78 self.client_managed.insert(value);
79 #[cfg(feature = "debug-capabilities")]
80 self.debug.register::<T>();
81 }
82
83 fn get_client_managed<T: RepositoryItem>(&self) -> Option<Arc<dyn Repository<T>>> {
85 self.client_managed.get()
86 }
87
88 fn get_sdk_managed<T: RepositoryItem>(
90 &self,
91 ) -> Result<Arc<dyn Repository<T>>, StateRegistryError> {
92 Ok(self.database.get_repository::<T>())
93 }
94
95 pub fn get<T>(&self) -> Result<Arc<dyn Repository<T>>, StateRegistryError>
103 where
104 T: RepositoryItem,
105 {
106 if let Some(repo) = self.get_client_managed::<T>() {
107 return Ok(repo);
108 }
109
110 self.get_sdk_managed::<T>()
111 }
112
113 pub async fn wipe(&self) -> Result<(), DatabaseError> {
123 self.client_managed.clear();
126 self.database.wipe().await
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::{
134 register_repository_item,
135 repository::{RepositoryError, RepositoryItem},
136 sdk_managed::DatabaseError,
137 };
138
139 macro_rules! impl_repository {
140 ($name:ident, $ty:ty) => {
141 #[async_trait::async_trait]
142 impl Repository<$ty> for $name {
143 async fn get(&self, _key: String) -> Result<Option<$ty>, RepositoryError> {
144 Ok(Some(TestItem(self.0.clone())))
145 }
146 async fn list(&self) -> Result<Vec<$ty>, RepositoryError> {
147 unimplemented!()
148 }
149 async fn set(&self, _key: String, _value: $ty) -> Result<(), RepositoryError> {
150 unimplemented!()
151 }
152 async fn set_bulk(
153 &self,
154 _values: Vec<(String, $ty)>,
155 ) -> Result<(), RepositoryError> {
156 unimplemented!()
157 }
158 async fn remove(&self, _key: String) -> Result<(), RepositoryError> {
159 unimplemented!()
160 }
161 async fn remove_bulk(&self, _keys: Vec<String>) -> Result<(), RepositoryError> {
162 unimplemented!()
163 }
164 async fn remove_all(&self) -> Result<(), RepositoryError> {
165 unimplemented!()
166 }
167 }
168 };
169 }
170
171 use serde::{Deserialize, Serialize};
172
173 #[derive(PartialEq, Eq, Debug)]
174 struct TestA(usize);
175 #[derive(PartialEq, Eq, Debug)]
176 struct TestB(String);
177 #[derive(PartialEq, Eq, Debug)]
178 struct TestC(Vec<u8>);
179 #[derive(PartialEq, Eq, Debug)]
181 struct TestD(usize);
182 #[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
183 struct TestItem<T>(T);
184
185 register_repository_item!(String => TestItem<usize>, "TestItem_usize");
186 register_repository_item!(String => TestItem<String>, "TestItem_String");
187 register_repository_item!(String => TestItem<Vec<u8>>, "TestItem_Vec");
188
189 impl_repository!(TestA, TestItem<usize>);
190 impl_repository!(TestB, TestItem<String>);
191 impl_repository!(TestC, TestItem<Vec<u8>>);
192 impl_repository!(TestD, TestItem<usize>);
193
194 #[tokio::test]
195 async fn test_state_registry() {
196 let a = Arc::new(TestA(145832));
197 let b = Arc::new(TestB("test".to_string()));
198 let c = Arc::new(TestC(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]));
199
200 let map = StateRegistry::new_with_memory_db();
201
202 async fn get<T: RepositoryItem>(map: &StateRegistry) -> Option<T>
203 where
204 T::Key: Default,
205 {
206 map.get_client_managed::<T>()
207 .unwrap()
208 .get(Default::default())
209 .await
210 .unwrap()
211 }
212
213 assert!(map.get_client_managed::<TestItem<usize>>().is_none());
214 assert!(map.get_client_managed::<TestItem<String>>().is_none());
215 assert!(map.get_client_managed::<TestItem<Vec<u8>>>().is_none());
216
217 map.register_client_managed(a.clone());
218 assert_eq!(get(&map).await, Some(TestItem(a.0)));
219 assert!(map.get_client_managed::<TestItem<String>>().is_none());
220 assert!(map.get_client_managed::<TestItem<Vec<u8>>>().is_none());
221
222 map.register_client_managed(b.clone());
223 assert_eq!(get(&map).await, Some(TestItem(a.0)));
224 assert_eq!(get(&map).await, Some(TestItem(b.0.clone())));
225 assert!(map.get_client_managed::<TestItem<Vec<u8>>>().is_none());
226
227 map.register_client_managed(c.clone());
228 assert_eq!(get(&map).await, Some(TestItem(a.0)));
229 assert_eq!(get(&map).await, Some(TestItem(b.0.clone())));
230 assert_eq!(get(&map).await, Some(TestItem(c.0.clone())));
231 }
232
233 #[tokio::test]
234 async fn test_fallback_client_managed_found() {
235 let registry = StateRegistry::new_with_memory_db();
236 let test_repo = Arc::new(TestA(12345));
237
238 registry.register_client_managed(test_repo.clone());
239
240 let repo = registry.get::<TestItem<usize>>().unwrap();
241 let result = repo.get(String::new()).await.unwrap();
242
243 assert_eq!(result, Some(TestItem(12345)));
244 }
245
246 #[tokio::test]
247 async fn test_new_with_memory_db_sync() {
248 let registry = StateRegistry::new_with_memory_db();
250 let repo = registry.get::<TestItem<usize>>().unwrap();
252 let result = repo.get(String::new()).await;
253 assert!(result.is_ok());
256 }
257
258 #[tokio::test]
259 async fn test_wipe_disconnects_outstanding_repository_handles() {
260 let registry = StateRegistry::new_with_memory_db();
261 let repo = registry.get::<TestItem<usize>>().unwrap();
262 repo.set(String::new(), TestItem(42usize)).await.unwrap();
263
264 registry.wipe().await.unwrap();
265
266 assert!(matches!(
267 repo.get(String::new()).await,
268 Err(RepositoryError::Database(DatabaseError::Closed))
269 ));
270 assert!(matches!(
271 repo.list().await,
272 Err(RepositoryError::Database(DatabaseError::Closed))
273 ));
274 }
275
276 #[tokio::test]
277 async fn test_wipe_clears_client_managed() {
278 let registry = StateRegistry::new_with_memory_db();
279 registry.register_client_managed(Arc::new(TestA(99)));
280
281 registry.wipe().await.unwrap();
282
283 let repo = registry.get::<TestItem<usize>>().unwrap();
285 assert!(matches!(
286 repo.get(String::new()).await,
287 Err(RepositoryError::Database(DatabaseError::Closed))
288 ));
289 }
290
291 #[tokio::test]
292 async fn test_wipe_is_idempotent() {
293 let registry = StateRegistry::new_with_memory_db();
294 registry.wipe().await.unwrap();
295 registry.wipe().await.unwrap();
296 }
297
298 #[tokio::test]
299 async fn test_persisted_value_on_memory_db() {
300 use crate::register_persisted_value;
301 register_persisted_value!(TEST_VALUE: String = "test_registry_persisted_value_key");
302
303 let registry = StateRegistry::new_with_memory_db();
304 let value = registry.value::<TEST_VALUE>();
305
306 assert_eq!(value.try_get().await.unwrap(), None::<String>);
308
309 value.set("hello".to_string()).await.unwrap();
311 assert_eq!(value.try_get().await.unwrap(), Some("hello".to_string()));
312
313 value.remove().await.unwrap();
315 assert_eq!(value.try_get().await.unwrap(), None::<String>);
316 }
317
318 #[tokio::test]
319 async fn test_persisted_values_are_isolated_by_key() {
320 use crate::register_persisted_value;
321 register_persisted_value!(THEME: String = "test_theme");
322 register_persisted_value!(LOCALE: String = "test_locale");
323
324 let registry = StateRegistry::new_with_memory_db();
325 let theme = registry.value::<THEME>();
326 let locale = registry.value::<LOCALE>();
327
328 theme.set("dark".to_string()).await.unwrap();
329 locale.set("en-US".to_string()).await.unwrap();
330
331 theme.remove().await.unwrap();
332 assert_eq!(theme.try_get().await.unwrap(), None::<String>);
333 assert_eq!(locale.try_get().await.unwrap(), Some("en-US".to_string()));
334 }
335
336 #[tokio::test]
337 async fn test_persisted_value_is_stored_in_the_persisted_value_table_as_bare_json() {
338 use crate::{persisted_value::PersistedValueItem, register_persisted_value};
339
340 #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
341 struct Config {
342 theme: String,
343 }
344 register_persisted_value!(CONFIG: Config = "test_config");
345
346 let registry = StateRegistry::new_with_memory_db();
347 let value = Config {
348 theme: "dark".to_string(),
349 };
350 let expected = serde_json::to_value(&value).unwrap();
351 registry.value::<CONFIG>().set(value).await.unwrap();
352
353 assert_eq!(PersistedValueItem::NAME, "PersistedValue");
356 let raw: PersistedValueItem = registry
357 .database
358 .get::<PersistedValueItem>("test_config")
359 .await
360 .unwrap()
361 .expect("persisted value is present");
362 assert_eq!(raw.0, expected);
363 }
364
365 #[tokio::test]
366 async fn test_persisted_value_reports_closed_after_wipe() {
367 use crate::{persisted_value::PersistedValueError, register_persisted_value};
368 register_persisted_value!(TEST_VALUE: String = "test_wiped_persisted_value");
369
370 let registry = StateRegistry::new_with_memory_db();
371 let value = registry.value::<TEST_VALUE>();
372 value.set("hello".to_string()).await.unwrap();
373
374 registry.wipe().await.unwrap();
375
376 assert!(matches!(
377 value.try_get().await,
378 Err(PersistedValueError::Closed)
379 ));
380 assert!(matches!(
381 value.set("bye".to_string()).await,
382 Err(PersistedValueError::Closed)
383 ));
384 }
385
386 #[tokio::test]
389 async fn test_register_client_managed_replaces_previous_implementation() {
390 let registry = StateRegistry::new_with_memory_db();
391
392 registry.register_client_managed(Arc::new(TestA(1)));
393 registry.register_client_managed(Arc::new(TestD(2)));
394
395 let repo = registry.get_client_managed::<TestItem<usize>>().unwrap();
396 assert_eq!(repo.get(String::new()).await.unwrap(), Some(TestItem(2)));
397 }
398}