use wasm_bindgen::prelude::*;
use uuid::Uuid;
use crate::{
vault::PrivacyVault,
VaultError,
VolumeConfig,
wasm::indexed_db_store::IndexedDbStore,
};
#[derive(serde::Serialize)]
pub struct AddStats {
pub real_added: usize,
pub dummies_added: usize,
}
#[derive(serde::Serialize)]
pub struct SearchStats {
pub results: Vec<String>,
pub real_evaluated: usize,
pub dummies_discarded: usize,
}
#[derive(serde::Serialize)]
pub struct DeleteStats {
pub tags_removed: usize,
pub tags_added: usize,
}
fn to_js_error(e: VaultError) -> JsValue {
JsValue::from_str(&e.to_string())
}
#[wasm_bindgen]
pub struct WasmVault {
vault: PrivacyVault,
store: IndexedDbStore,
}
#[wasm_bindgen]
impl WasmVault {
#[wasm_bindgen]
pub async fn new(password: &str, salt: &[u8]) -> Result<WasmVault, JsValue> {
let salt_array: [u8; 16] = salt.try_into().map_err(|_| "Invalid salt length")?;
let vault = PrivacyVault::new(password, &salt_array, VolumeConfig::default())
.map_err(to_js_error)?;
let store = IndexedDbStore::new().await.map_err(to_js_error)?;
Ok(WasmVault { vault, store })
}
pub async fn add_document(&mut self, keywords: JsValue, doc_id: &str) -> Result<JsValue, JsValue> {
let keywords: Vec<String> = serde_wasm_bindgen::from_value(keywords)?;
let keywords_str: Vec<&str> = keywords.iter().map(|s| s.as_str()).collect();
let doc_uuid = Uuid::parse_str(doc_id).map_err(|e| format!("Invalid UUID: {}", e))?;
self.vault
.add_document(&keywords_str, doc_uuid, &self.store)
.await
.map_err(to_js_error)?;
let n_max = self.vault.config.n_max;
let real_added = keywords.len();
let dummies_added = real_added * (n_max.saturating_sub(1));
let stats = AddStats { real_added, dummies_added };
Ok(serde_wasm_bindgen::to_value(&stats)?)
}
pub async fn search(&self, keyword: &str) -> Result<JsValue, JsValue> {
let results = self.vault.search(keyword, &self.store).await
.map_err(to_js_error)?;
let real_evaluated = results.len();
let n_max = self.vault.config.n_max;
let dummies_discarded = n_max.saturating_sub(real_evaluated);
let result_strs: Vec<String> = results.into_iter().map(|u| u.to_string()).collect();
let stats = SearchStats {
results: result_strs,
real_evaluated,
dummies_discarded,
};
Ok(serde_wasm_bindgen::to_value(&stats)?)
}
pub async fn delete_document(&mut self, keyword: &str, doc_id: &str) -> Result<JsValue, JsValue> {
let doc_uuid = Uuid::parse_str(doc_id).map_err(|e| format!("Invalid UUID: {}", e))?;
let (removed, added) = self.vault
.delete_document(keyword, doc_uuid, &self.store)
.await
.map_err(to_js_error)?;
let stats = DeleteStats { tags_removed: removed, tags_added: added };
Ok(serde_wasm_bindgen::to_value(&stats)?)
}
pub fn export_state(&self) -> Result<Vec<u8>, JsValue> {
self.vault.export_state().map_err(to_js_error)
}
}