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