bitwarden_state/sdk_managed/
mod.rs1use std::sync::Arc;
2
3use bitwarden_error::bitwarden_error;
4use thiserror::Error;
5
6use crate::repository::{Repository, RepositoryError, RepositoryItem, RepositoryMigrations};
7
8mod configuration;
9pub use configuration::DatabaseConfiguration;
10
11#[cfg(target_arch = "wasm32")]
12mod indexed_db;
13
14#[cfg(not(target_arch = "wasm32"))]
15mod sqlite;
16
17mod memory;
18pub(super) use memory::MemoryDatabase;
19
20#[bitwarden_error(flat)]
22#[derive(Debug, Error)]
23pub enum DatabaseError {
24 #[error("Database not supported on this platform: {0:?}")]
26 UnsupportedConfiguration(DatabaseConfiguration),
27
28 #[error(transparent)]
30 ThreadBoundRunner(#[from] bitwarden_threading::CallError),
31
32 #[error("Serialization error: {0}")]
34 Serialization(#[from] serde_json::Error),
35
36 #[error("JS error: {0}")]
38 JS(String),
39
40 #[error("Internal error: {0}")]
42 Internal(String),
43
44 #[error("Database is closed")]
47 Closed,
48}
49
50#[cfg(target_arch = "wasm32")]
51impl From<::indexed_db::Error<indexed_db::IndexedDbInternalError>> for DatabaseError {
52 fn from(e: ::indexed_db::Error<indexed_db::IndexedDbInternalError>) -> Self {
53 DatabaseError::Internal(e.to_string())
54 }
55}
56
57#[cfg(not(target_arch = "wasm32"))]
58impl From<rusqlite::Error> for DatabaseError {
59 fn from(e: rusqlite::Error) -> Self {
60 DatabaseError::Internal(e.to_string())
61 }
62}
63
64pub trait Database {
65 async fn initialize(
66 configuration: DatabaseConfiguration,
67 registrations: RepositoryMigrations,
68 ) -> Result<Self, DatabaseError>
69 where
70 Self: Sized;
71
72 async fn get<T: RepositoryItem>(&self, key: &str) -> Result<Option<T>, DatabaseError>;
73
74 async fn list<T: RepositoryItem>(&self) -> Result<Vec<T>, DatabaseError>;
75
76 async fn set<T: RepositoryItem>(&self, key: &str, value: T) -> Result<(), DatabaseError>;
77
78 async fn set_bulk<T: RepositoryItem>(
79 &self,
80 values: Vec<(String, T)>,
81 ) -> Result<(), DatabaseError>;
82
83 async fn remove<T: RepositoryItem>(&self, key: &str) -> Result<(), DatabaseError>;
84
85 async fn remove_bulk<T: RepositoryItem>(&self, keys: Vec<String>) -> Result<(), DatabaseError>;
86
87 async fn remove_all<T: RepositoryItem>(&self) -> Result<(), DatabaseError>;
88
89 async fn wipe(&self) -> Result<(), DatabaseError>;
90}
91
92#[derive(Clone)]
93pub(super) enum SystemDatabase {
94 #[cfg(not(target_arch = "wasm32"))]
95 Sqlite(sqlite::SqliteDatabase),
96 #[cfg(target_arch = "wasm32")]
97 IndexedDb(indexed_db::IndexedDbDatabase),
98 Memory(MemoryDatabase),
99}
100
101impl Database for SystemDatabase {
102 async fn initialize(
103 configuration: DatabaseConfiguration,
104 migrations: RepositoryMigrations,
105 ) -> Result<Self, DatabaseError> {
106 match configuration {
107 #[cfg(not(target_arch = "wasm32"))]
108 DatabaseConfiguration::Sqlite { .. } => Ok(SystemDatabase::Sqlite(
109 sqlite::SqliteDatabase::initialize(configuration, migrations).await?,
110 )),
111 #[cfg(target_arch = "wasm32")]
112 DatabaseConfiguration::IndexedDb { .. } => Ok(SystemDatabase::IndexedDb(
113 indexed_db::IndexedDbDatabase::initialize(configuration, migrations).await?,
114 )),
115 DatabaseConfiguration::Memory => Ok(SystemDatabase::Memory(MemoryDatabase::new())),
116 #[allow(unreachable_patterns)]
117 other => Err(DatabaseError::UnsupportedConfiguration(other)),
118 }
119 }
120
121 async fn get<T: RepositoryItem>(&self, key: &str) -> Result<Option<T>, DatabaseError> {
122 match self {
123 #[cfg(not(target_arch = "wasm32"))]
124 SystemDatabase::Sqlite(db) => db.get(key).await,
125 #[cfg(target_arch = "wasm32")]
126 SystemDatabase::IndexedDb(db) => db.get(key).await,
127 SystemDatabase::Memory(db) => db.get(key).await,
128 }
129 }
130
131 async fn list<T: RepositoryItem>(&self) -> Result<Vec<T>, DatabaseError> {
132 match self {
133 #[cfg(not(target_arch = "wasm32"))]
134 SystemDatabase::Sqlite(db) => db.list().await,
135 #[cfg(target_arch = "wasm32")]
136 SystemDatabase::IndexedDb(db) => db.list().await,
137 SystemDatabase::Memory(db) => db.list().await,
138 }
139 }
140
141 async fn set<T: RepositoryItem>(&self, key: &str, value: T) -> Result<(), DatabaseError> {
142 match self {
143 #[cfg(not(target_arch = "wasm32"))]
144 SystemDatabase::Sqlite(db) => db.set(key, value).await,
145 #[cfg(target_arch = "wasm32")]
146 SystemDatabase::IndexedDb(db) => db.set(key, value).await,
147 SystemDatabase::Memory(db) => db.set(key, value).await,
148 }
149 }
150
151 async fn set_bulk<T: RepositoryItem>(
152 &self,
153 values: Vec<(String, T)>,
154 ) -> Result<(), DatabaseError> {
155 match self {
156 #[cfg(not(target_arch = "wasm32"))]
157 SystemDatabase::Sqlite(db) => db.set_bulk(values).await,
158 #[cfg(target_arch = "wasm32")]
159 SystemDatabase::IndexedDb(db) => db.set_bulk(values).await,
160 SystemDatabase::Memory(db) => db.set_bulk(values).await,
161 }
162 }
163
164 async fn remove<T: RepositoryItem>(&self, key: &str) -> Result<(), DatabaseError> {
165 match self {
166 #[cfg(not(target_arch = "wasm32"))]
167 SystemDatabase::Sqlite(db) => db.remove::<T>(key).await,
168 #[cfg(target_arch = "wasm32")]
169 SystemDatabase::IndexedDb(db) => db.remove::<T>(key).await,
170 SystemDatabase::Memory(db) => db.remove::<T>(key).await,
171 }
172 }
173
174 async fn remove_bulk<T: RepositoryItem>(&self, keys: Vec<String>) -> Result<(), DatabaseError> {
175 match self {
176 #[cfg(not(target_arch = "wasm32"))]
177 SystemDatabase::Sqlite(db) => db.remove_bulk::<T>(keys).await,
178 #[cfg(target_arch = "wasm32")]
179 SystemDatabase::IndexedDb(db) => db.remove_bulk::<T>(keys).await,
180 SystemDatabase::Memory(db) => db.remove_bulk::<T>(keys).await,
181 }
182 }
183
184 async fn remove_all<T: RepositoryItem>(&self) -> Result<(), DatabaseError> {
185 match self {
186 #[cfg(not(target_arch = "wasm32"))]
187 SystemDatabase::Sqlite(db) => db.remove_all::<T>().await,
188 #[cfg(target_arch = "wasm32")]
189 SystemDatabase::IndexedDb(db) => db.remove_all::<T>().await,
190 SystemDatabase::Memory(db) => db.remove_all::<T>().await,
191 }
192 }
193
194 async fn wipe(&self) -> Result<(), DatabaseError> {
195 match self {
196 #[cfg(not(target_arch = "wasm32"))]
197 SystemDatabase::Sqlite(db) => db.wipe().await,
198 #[cfg(target_arch = "wasm32")]
199 SystemDatabase::IndexedDb(db) => db.wipe().await,
200 SystemDatabase::Memory(db) => db.wipe().await,
201 }
202 }
203}
204
205struct DBRepository<T: RepositoryItem> {
206 database: SystemDatabase,
207 _marker: std::marker::PhantomData<T>,
208}
209
210#[async_trait::async_trait]
211impl<V: RepositoryItem> Repository<V> for DBRepository<V> {
212 async fn get(&self, key: V::Key) -> Result<Option<V>, RepositoryError> {
213 let key = key.to_string();
214 let value = self.database.get::<V>(&key).await?;
215 Ok(value)
216 }
217 async fn list(&self) -> Result<Vec<V>, RepositoryError> {
218 let values = self.database.list::<V>().await?;
219 Ok(values)
220 }
221 async fn set(&self, key: V::Key, value: V) -> Result<(), RepositoryError> {
222 let key = key.to_string();
223 Ok(self.database.set::<V>(&key, value).await?)
224 }
225 async fn set_bulk(&self, values: Vec<(V::Key, V)>) -> Result<(), RepositoryError> {
226 let values = values
227 .into_iter()
228 .map(|(k, v)| (k.to_string(), v))
229 .collect();
230 Ok(self.database.set_bulk::<V>(values).await?)
231 }
232 async fn remove(&self, key: V::Key) -> Result<(), RepositoryError> {
233 let key = key.to_string();
234 Ok(self.database.remove::<V>(&key).await?)
235 }
236 async fn remove_bulk(&self, keys: Vec<V::Key>) -> Result<(), RepositoryError> {
237 let keys = keys.into_iter().map(|k| k.to_string()).collect();
238 Ok(self.database.remove_bulk::<V>(keys).await?)
239 }
240 async fn remove_all(&self) -> Result<(), RepositoryError> {
241 Ok(self.database.remove_all::<V>().await?)
242 }
243}
244
245impl SystemDatabase {
246 pub(super) fn get_repository<V: RepositoryItem>(&self) -> Arc<dyn Repository<V>> {
247 Arc::new(DBRepository {
248 database: self.clone(),
249 _marker: std::marker::PhantomData,
250 })
251 }
252}