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