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 repository::{Repository, RepositoryError, RepositoryItem, RepositoryMigrations},
9 settings::{SettingItem, SettingTrait, SettingsError},
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 DBSetting<T> {
251 database: SystemDatabase,
252 name: &'static str,
253 _marker: std::marker::PhantomData<T>,
254}
255
256#[async_trait::async_trait]
257impl<T: Persist> SettingTrait<T> for DBSetting<T> {
258 async fn get(&self) -> Result<Option<T>, SettingsError> {
259 match self.database.get::<SettingItem>(self.name).await? {
260 Some(item) => Ok(Some(serde_json::from_value::<T>(item.0)?)),
261 None => Ok(None),
262 }
263 }
264
265 async fn set(&self, value: T) -> Result<(), SettingsError> {
266 let item = SettingItem(serde_json::to_value(&value)?);
267 Ok(self.database.set::<SettingItem>(self.name, item).await?)
268 }
269
270 async fn remove(&self) -> Result<(), SettingsError> {
271 Ok(self.database.remove::<SettingItem>(self.name).await?)
272 }
273}
274
275impl SystemDatabase {
276 pub(super) fn get_repository<V: RepositoryItem>(&self) -> Arc<dyn Repository<V>> {
277 Arc::new(DBRepository {
278 database: self.clone(),
279 _marker: std::marker::PhantomData,
280 })
281 }
282
283 pub(super) fn get_setting<T: Persist>(&self, name: &'static str) -> Arc<dyn SettingTrait<T>> {
284 Arc::new(DBSetting {
285 database: self.clone(),
286 name,
287 _marker: std::marker::PhantomData,
288 })
289 }
290}