Skip to main content

bitwarden_state/sdk_managed/
mod.rs

1use 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}