use super::connection::IndexedDbConnection;
use super::errors::IndexedDbError;
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::JsFuture;
#[cfg(target_arch = "wasm32")]
use web_sys::{IdbObjectStore, IdbRequest, IdbRequestReadyState, IdbTransaction};
pub struct IndexedDbOperations {
connection: Arc<IndexedDbConnection>,
}
impl IndexedDbOperations {
pub fn new(connection: Arc<IndexedDbConnection>) -> Self {
Self { connection }
}
#[cfg(target_arch = "wasm32")]
pub async fn get<T: DeserializeOwned>(
&self,
store_name: &str,
key: &str,
) -> Result<Option<T>, IndexedDbError> {
let transaction = self.connection.readonly_transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
let request = store.get(&JsValue::from_str(key)).map_err(|_| {
IndexedDbError::RequestError("Failed to create get request".to_string())
})?;
let result = JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute get request".to_string())
})?;
if result.is_undefined() {
Ok(None)
} else {
let data = js_sys::Uint8Array::new(&result);
let bytes = data.to_vec();
let value = serde_json::from_slice(&bytes)
.map_err(|e| IndexedDbError::SerializationError(e.to_string()))?;
Ok(Some(value))
}
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn get<T: DeserializeOwned>(
&self,
_store_name: &str,
_key: &str,
) -> Result<Option<T>, IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
#[cfg(target_arch = "wasm32")]
pub async fn set<T: Serialize>(
&self,
store_name: &str,
key: &str,
value: &T,
) -> Result<(), IndexedDbError> {
let transaction = self.connection.transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
let serialized = serde_json::to_vec(value)
.map_err(|e| IndexedDbError::SerializationError(e.to_string()))?;
let array = js_sys::Uint8Array::from(serialized.as_slice());
let request = store
.put_key_val(&JsValue::from_str(key), &array.into())
.map_err(|_| {
IndexedDbError::RequestError("Failed to create put request".to_string())
})?;
JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute put request".to_string())
})?;
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn set<T: Serialize>(
&self,
_store_name: &str,
_key: &str,
_value: &T,
) -> Result<(), IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
#[cfg(target_arch = "wasm32")]
pub async fn delete(&self, store_name: &str, key: &str) -> Result<(), IndexedDbError> {
let transaction = self.connection.transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
let request = store.delete(&JsValue::from_str(key)).map_err(|_| {
IndexedDbError::RequestError("Failed to create delete request".to_string())
})?;
JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute delete request".to_string())
})?;
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn delete(&self, _store_name: &str, _key: &str) -> Result<(), IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
#[cfg(target_arch = "wasm32")]
pub async fn keys(&self, store_name: &str) -> Result<Vec<String>, IndexedDbError> {
let transaction = self.connection.readonly_transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
let request = store.get_all_keys().map_err(|_| {
IndexedDbError::RequestError("Failed to create get_all_keys request".to_string())
})?;
let result = JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute get_all_keys request".to_string())
})?;
let keys_array = js_sys::Array::from(&result);
let mut keys = Vec::new();
for i in 0..keys_array.length() {
if let Some(key) = keys_array.get(i).as_string() {
keys.push(key);
}
}
Ok(keys)
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn keys(&self, _store_name: &str) -> Result<Vec<String>, IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
#[cfg(target_arch = "wasm32")]
pub async fn contains_key(&self, store_name: &str, key: &str) -> Result<bool, IndexedDbError> {
let transaction = self.connection.readonly_transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
let request = store.count_with_key(&JsValue::from_str(key)).map_err(|_| {
IndexedDbError::RequestError("Failed to create count request".to_string())
})?;
let result = JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute count request".to_string())
})?;
let count = result.as_f64().unwrap_or(0.0) as u32;
Ok(count > 0)
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn contains_key(
&self,
_store_name: &str,
_key: &str,
) -> Result<bool, IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
#[cfg(target_arch = "wasm32")]
pub async fn clear(&self, store_name: &str) -> Result<(), IndexedDbError> {
let transaction = self.connection.transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
let request = store.clear().map_err(|_| {
IndexedDbError::RequestError("Failed to create clear request".to_string())
})?;
JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute clear request".to_string())
})?;
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn clear(&self, _store_name: &str) -> Result<(), IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
#[cfg(target_arch = "wasm32")]
pub async fn count(&self, store_name: &str) -> Result<usize, IndexedDbError> {
let transaction = self.connection.readonly_transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
let request = store.count().map_err(|_| {
IndexedDbError::RequestError("Failed to create count request".to_string())
})?;
let result = JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute count request".to_string())
})?;
let count = result.as_f64().unwrap_or(0.0) as usize;
Ok(count)
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn count(&self, _store_name: &str) -> Result<usize, IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
#[cfg(target_arch = "wasm32")]
pub async fn batch_set<T: Serialize>(
&self,
store_name: &str,
items: &[(String, T)],
) -> Result<(), IndexedDbError> {
if items.is_empty() {
return Ok(());
}
let transaction = self.connection.transaction(&[store_name])?;
let store = transaction.object_store(store_name).map_err(|_| {
IndexedDbError::ObjectStoreError("Failed to get object store".to_string())
})?;
for (key, value) in items {
let serialized = serde_json::to_vec(value)
.map_err(|e| IndexedDbError::SerializationError(e.to_string()))?;
let array = js_sys::Uint8Array::from(serialized.as_slice());
let request = store
.put_key_val(&JsValue::from_str(key), &array.into())
.map_err(|_| {
IndexedDbError::RequestError("Failed to create put request".to_string())
})?;
JsFuture::from(request).await.map_err(|_| {
IndexedDbError::RequestError("Failed to execute put request".to_string())
})?;
}
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn batch_set<T: Serialize>(
&self,
_store_name: &str,
_items: &[(String, T)],
) -> Result<(), IndexedDbError> {
Err(IndexedDbError::NotSupported(
"IndexedDB not available in non-WASM environment".to_string(),
))
}
}
impl Clone for IndexedDbOperations {
fn clone(&self) -> Self {
Self {
connection: self.connection.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_operations_creation() {
#[cfg(not(target_arch = "wasm32"))]
{
let result = IndexedDbConnection::open("test_db", 1).await;
assert!(result.is_err());
}
}
}