1
1
use std:: {
2
2
any:: { Any , TypeId } ,
3
3
collections:: HashMap ,
4
- sync:: { Arc , RwLock } ,
4
+ sync:: { Arc , OnceLock , RwLock } ,
5
5
} ;
6
6
7
- use crate :: repository:: { Repository , RepositoryItem } ;
7
+ use bitwarden_error:: bitwarden_error;
8
+ use serde:: { de:: DeserializeOwned , Serialize } ;
9
+ use thiserror:: Error ;
10
+
11
+ use crate :: {
12
+ repository:: { Repository , RepositoryItem , RepositoryItemData } ,
13
+ sdk_managed:: { Database , SystemDatabase } ,
14
+ } ;
8
15
9
16
/// A registry that contains repositories for different types of items.
10
17
/// These repositories can be either managed by the client or by the SDK itself.
11
18
pub struct StateRegistry {
19
+ sdk_managed : RwLock < Vec < RepositoryItemData > > ,
12
20
client_managed : RwLock < HashMap < TypeId , Box < dyn Any + Send + Sync > > > ,
21
+
22
+ database : OnceLock < SystemDatabase > ,
13
23
}
14
24
15
25
impl std:: fmt:: Debug for StateRegistry {
@@ -18,13 +28,58 @@ impl std::fmt::Debug for StateRegistry {
18
28
}
19
29
}
20
30
31
+ #[ allow( missing_docs) ]
32
+ #[ bitwarden_error( flat) ]
33
+ #[ derive( Debug , Error ) ]
34
+ pub enum StateRegistryError {
35
+ #[ error( "Database is already initialized" ) ]
36
+ DatabaseAlreadyInitialized ,
37
+ #[ error( "Database is not initialized" ) ]
38
+ DatabaseNotInitialized ,
39
+
40
+ #[ error( transparent) ]
41
+ Database ( #[ from] crate :: sdk_managed:: DatabaseError ) ,
42
+ }
43
+
21
44
impl StateRegistry {
22
45
/// Creates a new empty `StateRegistry`.
23
46
#[ allow( clippy:: new_without_default) ]
24
47
pub fn new ( ) -> Self {
25
48
StateRegistry {
26
49
client_managed : RwLock :: new ( HashMap :: new ( ) ) ,
50
+ database : OnceLock :: new ( ) ,
51
+ sdk_managed : RwLock :: new ( Vec :: new ( ) ) ,
52
+ }
53
+ }
54
+
55
+ // TODO: Ideally we'd do this in new, but that would mean making the client initialization
56
+ // async.
57
+ // TODO: This function needs to be provided some configuration to know where to open the
58
+ // database. For Sqlite:
59
+ // - A folder path where the files will be stored.
60
+ // - A user ID to create a unique database file per user?
61
+ //
62
+ // For WASM indexedDB:
63
+ // - A database name to use for the indexedDB (Some prefix to avoid conflicts + user ID?)
64
+
65
+ /// Initializes the database used for sdk-managed repositories.
66
+ pub async fn initialize_database (
67
+ & self ,
68
+ repositories : Vec < RepositoryItemData > ,
69
+ ) -> Result < ( ) , StateRegistryError > {
70
+ if self . database . get ( ) . is_some ( ) {
71
+ return Err ( StateRegistryError :: DatabaseAlreadyInitialized ) ;
27
72
}
73
+ let _ = self
74
+ . database
75
+ . set ( SystemDatabase :: initialize ( & repositories) . await ?) ;
76
+
77
+ * self
78
+ . sdk_managed
79
+ . write ( )
80
+ . expect ( "RwLock should not be poisoned" ) = repositories. clone ( ) ;
81
+
82
+ Ok ( ( ) )
28
83
}
29
84
30
85
/// Registers a client-managed repository into the map, associating it with its type.
@@ -44,6 +99,17 @@ impl StateRegistry {
44
99
. and_then ( |boxed| boxed. downcast_ref :: < Arc < dyn Repository < T > > > ( ) )
45
100
. map ( Arc :: clone)
46
101
}
102
+
103
+ /// Retrieves a SDK-managed repository from the database.
104
+ pub fn get_sdk_managed < T : RepositoryItem + Serialize + DeserializeOwned > (
105
+ & self ,
106
+ ) -> Result < impl Repository < T > , StateRegistryError > {
107
+ if let Some ( db) = self . database . get ( ) {
108
+ Ok ( db. get_repository :: < T > ( ) ?)
109
+ } else {
110
+ Err ( StateRegistryError :: DatabaseNotInitialized )
111
+ }
112
+ }
47
113
}
48
114
49
115
#[ cfg( test) ]
@@ -83,9 +149,9 @@ mod tests {
83
149
#[ derive( PartialEq , Eq , Debug ) ]
84
150
struct TestItem < T > ( T ) ;
85
151
86
- register_repository_item ! ( TestItem <usize >, "TestItem<usize>" ) ;
87
- register_repository_item ! ( TestItem <String >, "TestItem<String>" ) ;
88
- register_repository_item ! ( TestItem <Vec <u8 >>, "TestItem<Vec<u8>>" ) ;
152
+ register_repository_item ! ( TestItem <usize >, "TestItem<usize>" , version : 1 ) ;
153
+ register_repository_item ! ( TestItem <String >, "TestItem<String>" , version : 1 ) ;
154
+ register_repository_item ! ( TestItem <Vec <u8 >>, "TestItem<Vec<u8>>" , version : 1 ) ;
89
155
90
156
impl_repository ! ( TestA , TestItem <usize >) ;
91
157
impl_repository ! ( TestB , TestItem <String >) ;
0 commit comments