Skip to content

Commit b9dabb5

Browse files
committed
Don't stringify the data in indexeddb
1 parent 5b9dd03 commit b9dabb5

File tree

5 files changed

+97
-37
lines changed

5 files changed

+97
-37
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bitwarden-state/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ thiserror = { workspace = true }
2323
tokio = { workspace = true }
2424

2525
[target.'cfg(target_arch="wasm32")'.dependencies]
26-
js-sys = { workspace = true }
2726
indexed-db = ">=0.4.2, <0.5"
27+
js-sys = { workspace = true }
28+
tsify-next = { workspace = true }
29+
2830
[target.'cfg(not(target_arch="wasm32"))'.dependencies]
2931
rusqlite = { version = ">=0.36.0, <0.37", features = ["bundled"] }
3032

crates/bitwarden-state/src/sdk_managed/indexed_db.rs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
1-
use std::convert::Infallible;
2-
31
use js_sys::JsString;
2+
use serde::{de::DeserializeOwned, ser::Serialize};
43

54
use crate::{
6-
repository::RepositoryItemData,
5+
repository::{RepositoryItem, RepositoryItemData},
76
sdk_managed::{Database, DatabaseError},
87
};
98

9+
#[derive(Debug, thiserror::Error)]
10+
#[error("IndexedDB internal error: {0}")]
11+
pub struct IndexedDbInternalError(String);
12+
impl From<tsify_next::serde_wasm_bindgen::Error> for IndexedDbInternalError {
13+
fn from(err: tsify_next::serde_wasm_bindgen::Error) -> Self {
14+
IndexedDbInternalError(err.to_string())
15+
}
16+
}
17+
1018
#[derive(Clone)]
1119
pub struct IndexedDbDatabase(
12-
bitwarden_threading::ThreadBoundRunner<indexed_db::Database<Infallible>>,
20+
bitwarden_threading::ThreadBoundRunner<indexed_db::Database<IndexedDbInternalError>>,
1321
);
1422
impl Database for IndexedDbDatabase {
1523
async fn initialize(registrations: &[RepositoryItemData]) -> Result<Self, DatabaseError> {
16-
let factory = indexed_db::Factory::<Infallible>::get()?;
24+
let factory = indexed_db::Factory::get()?;
1725

1826
let registrations = registrations.to_vec();
1927

@@ -39,7 +47,11 @@ impl Database for IndexedDbDatabase {
3947
Ok(IndexedDbDatabase(runner))
4048
}
4149

42-
async fn get(&self, namespace: &str, key: &str) -> Result<Option<String>, DatabaseError> {
50+
async fn get<T: Serialize + DeserializeOwned + RepositoryItem>(
51+
&self,
52+
namespace: &str,
53+
key: &str,
54+
) -> Result<Option<T>, DatabaseError> {
4355
let namespace = namespace.to_string();
4456
let key = key.to_string();
4557

@@ -52,7 +64,8 @@ impl Database for IndexedDbDatabase {
5264
let response = store.get(&JsString::from(key)).await?;
5365

5466
if let Some(value) = response {
55-
Ok(value.as_string())
67+
Ok(::tsify_next::serde_wasm_bindgen::from_value(value)
68+
.map_err(IndexedDbInternalError::from)?)
5669
} else {
5770
Ok(None)
5871
}
@@ -64,7 +77,10 @@ impl Database for IndexedDbDatabase {
6477
Ok(result)
6578
}
6679

67-
async fn list(&self, namespace: &str) -> Result<Vec<String>, DatabaseError> {
80+
async fn list<T: Serialize + DeserializeOwned + RepositoryItem>(
81+
&self,
82+
namespace: &str,
83+
) -> Result<Vec<T>, DatabaseError> {
6884
let namespace = namespace.to_string();
6985

7086
let results = self
@@ -75,10 +91,13 @@ impl Database for IndexedDbDatabase {
7591
let store = t.object_store(&namespace)?;
7692
let results = store.get_all(None).await?;
7793

78-
let items: Vec<String> = results
79-
.into_iter()
80-
.filter_map(|item| item.as_string())
81-
.collect();
94+
let mut items: Vec<T> = Vec::new();
95+
96+
for value in results {
97+
let item: T = ::tsify_next::serde_wasm_bindgen::from_value(value)
98+
.map_err(IndexedDbInternalError::from)?;
99+
items.push(item);
100+
}
82101

83102
Ok(items)
84103
})
@@ -89,20 +108,26 @@ impl Database for IndexedDbDatabase {
89108
Ok(results)
90109
}
91110

92-
async fn set(&self, namespace: &str, key: &str, value: String) -> Result<(), DatabaseError> {
111+
async fn set<T: Serialize + DeserializeOwned + RepositoryItem>(
112+
&self,
113+
namespace: &str,
114+
key: &str,
115+
value: T,
116+
) -> Result<(), DatabaseError> {
93117
let namespace = namespace.to_string();
94118
let key = key.to_string();
95-
let value = value.to_string();
96119

97120
self.0
98121
.run_in_thread(move |db| async move {
99122
db.transaction(&[&namespace])
100123
.rw()
101124
.run(|t| async move {
102125
let store = t.object_store(&namespace)?;
103-
store
104-
.put_kv(&JsString::from(key), &JsString::from(value))
105-
.await?;
126+
127+
let value = ::tsify_next::serde_wasm_bindgen::to_value(&value)
128+
.map_err(IndexedDbInternalError::from)?;
129+
130+
store.put_kv(&JsString::from(key), &value).await?;
106131
Ok(())
107132
})
108133
.await

crates/bitwarden-state/src/sdk_managed/mod.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use thiserror::Error;
44

55
use crate::repository::{Repository, RepositoryError, RepositoryItem, RepositoryItemData};
66

7-
#[cfg(target_arch = "wasm32")]
7+
//#[cfg(target_arch = "wasm32")]
88
mod indexed_db;
99
#[cfg(target_arch = "wasm32")]
1010
pub(super) type SystemDatabase = indexed_db::IndexedDbDatabase;
1111
#[cfg(target_arch = "wasm32")]
12-
type InternalError = ::indexed_db::Error<std::convert::Infallible>;
12+
type InternalError = ::indexed_db::Error<indexed_db::IndexedDbInternalError>;
1313

1414
#[cfg(not(target_arch = "wasm32"))]
1515
mod sqlite;
@@ -24,6 +24,12 @@ pub enum DatabaseError {
2424
#[error(transparent)]
2525
ThreadBoundRunner(#[from] bitwarden_threading::CallError),
2626

27+
#[error("Serialization error: {0}")]
28+
Serialization(#[from] serde_json::Error),
29+
30+
#[error("JS error: {0}")]
31+
JSError(String),
32+
2733
#[error(transparent)]
2834
Internal(#[from] InternalError),
2935
}
@@ -33,11 +39,23 @@ pub trait Database {
3339
where
3440
Self: Sized;
3541

36-
async fn get(&self, namespace: &str, key: &str) -> Result<Option<String>, DatabaseError>;
42+
async fn get<T: Serialize + DeserializeOwned + RepositoryItem>(
43+
&self,
44+
namespace: &str,
45+
key: &str,
46+
) -> Result<Option<T>, DatabaseError>;
3747

38-
async fn list(&self, namespace: &str) -> Result<Vec<String>, DatabaseError>;
48+
async fn list<T: Serialize + DeserializeOwned + RepositoryItem>(
49+
&self,
50+
namespace: &str,
51+
) -> Result<Vec<T>, DatabaseError>;
3952

40-
async fn set(&self, namespace: &str, key: &str, value: String) -> Result<(), DatabaseError>;
53+
async fn set<T: Serialize + DeserializeOwned + RepositoryItem>(
54+
&self,
55+
namespace: &str,
56+
key: &str,
57+
value: T,
58+
) -> Result<(), DatabaseError>;
4159

4260
async fn remove(&self, namespace: &str, key: &str) -> Result<(), DatabaseError>;
4361
}
@@ -51,19 +69,14 @@ struct DBRepository<T: RepositoryItem> {
5169
impl<V: RepositoryItem + Serialize + DeserializeOwned> Repository<V> for DBRepository<V> {
5270
async fn get(&self, key: String) -> Result<Option<V>, RepositoryError> {
5371
let value = self.database.get(V::NAME, &key).await?;
54-
Ok(value.map(|v| serde_json::from_str(&v)).transpose()?)
72+
Ok(value)
5573
}
5674
async fn list(&self) -> Result<Vec<V>, RepositoryError> {
5775
let values = self.database.list(V::NAME).await?;
58-
let mut results = Vec::new();
59-
for value in values {
60-
results.push(serde_json::from_str(&value)?);
61-
}
62-
Ok(results)
76+
Ok(values)
6377
}
6478
async fn set(&self, key: String, value: V) -> Result<(), RepositoryError> {
65-
let value_str = serde_json::to_string(&value)?;
66-
Ok(self.database.set(V::NAME, &key, value_str).await?)
79+
Ok(self.database.set(V::NAME, &key, value).await?)
6780
}
6881
async fn remove(&self, key: String) -> Result<(), RepositoryError> {
6982
Ok(self.database.remove(V::NAME, &key).await?)

crates/bitwarden-state/src/sdk_managed/sqlite.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::sync::Arc;
22

3+
use serde::{de::DeserializeOwned, ser::Serialize};
34
use tokio::sync::Mutex;
45

56
use crate::{
6-
repository::RepositoryItemData,
7+
repository::{RepositoryItem, RepositoryItemData},
78
sdk_managed::{Database, DatabaseError},
89
};
910

@@ -30,35 +31,53 @@ impl Database for SqliteDatabase {
3031
Ok(SqliteDatabase(Arc::new(Mutex::new(db))))
3132
}
3233

33-
async fn get(&self, namespace: &str, key: &str) -> Result<Option<String>, DatabaseError> {
34+
async fn get<T: Serialize + DeserializeOwned + RepositoryItem>(
35+
&self,
36+
namespace: &str,
37+
key: &str,
38+
) -> Result<Option<T>, DatabaseError> {
3439
let conn = self.0.lock().await;
3540
let mut stmt = conn.prepare("SELECT value FROM ?1 WHERE key = ?2")?;
3641
let mut rows = stmt.query(rusqlite::params![namespace, key])?;
3742

3843
if let Some(row) = rows.next()? {
39-
Ok(Some(row.get(0)?))
44+
let value = row.get::<_, String>(0)?;
45+
46+
Ok(Some(serde_json::from_str(&value)?))
4047
} else {
4148
Ok(None)
4249
}
4350
}
4451

45-
async fn list(&self, namespace: &str) -> Result<Vec<String>, DatabaseError> {
52+
async fn list<T: Serialize + DeserializeOwned + RepositoryItem>(
53+
&self,
54+
namespace: &str,
55+
) -> Result<Vec<T>, DatabaseError> {
4656
let conn = self.0.lock().await;
4757
let mut stmt = conn.prepare("SELECT key, value FROM ?1")?;
4858
let rows = stmt.query_map(rusqlite::params![namespace], |row| row.get(1))?;
4959

5060
let mut results = Vec::new();
5161
for row in rows {
52-
results.push(row?);
62+
let value: String = row?;
63+
let value: T = serde_json::from_str(&value)?;
64+
results.push(value);
5365
}
5466

5567
Ok(results)
5668
}
5769

58-
async fn set(&self, namespace: &str, key: &str, value: String) -> Result<(), DatabaseError> {
70+
async fn set<T: Serialize + DeserializeOwned + RepositoryItem>(
71+
&self,
72+
namespace: &str,
73+
key: &str,
74+
value: T,
75+
) -> Result<(), DatabaseError> {
5976
let mut conn = self.0.lock().await;
6077
let transaction = conn.transaction()?;
6178

79+
let value = serde_json::to_string(&value)?;
80+
6281
transaction.execute(
6382
"INSERT OR REPLACE INTO ?1 (key, value) VALUES (?2, ?3)",
6483
rusqlite::params![namespace, key, value],

0 commit comments

Comments
 (0)