leptos_sync_core/storage/
indexeddb.rs

1//! IndexedDB storage implementation
2
3use super::{LocalStorage, StorageError};
4use async_trait::async_trait;
5use serde::{de::DeserializeOwned, Serialize};
6use wasm_bindgen::JsCast;
7use web_sys::{window, Storage};
8
9/// IndexedDB storage implementation (currently falls back to localStorage)
10pub struct IndexedDbStorage {
11    db_name: String,
12    store_name: String,
13    fallback: super::memory::MemoryStorage,
14}
15
16impl IndexedDbStorage {
17    pub fn new(db_name: String, store_name: String) -> Self {
18        Self {
19            db_name,
20            store_name,
21            fallback: super::memory::MemoryStorage::new(),
22        }
23    }
24
25    /// Get the localStorage fallback
26    fn get_local_storage() -> Result<Storage, StorageError> {
27        let window = window().ok_or_else(|| {
28            StorageError::OperationFailed("No window available".to_string())
29        })?;
30        
31        let storage = window.local_storage().map_err(|_| {
32            StorageError::OperationFailed("Failed to get localStorage".to_string())
33        })?.ok_or_else(|| {
34            StorageError::OperationFailed("localStorage not available".to_string())
35        })?;
36        
37        Ok(storage)
38    }
39
40    /// Get a value from localStorage
41    fn get_from_local_storage<T: DeserializeOwned>(key: &str) -> Result<Option<T>, StorageError> {
42        let storage = Self::get_local_storage()?;
43        
44        match storage.get_item(key).map_err(|_| {
45            StorageError::OperationFailed("Failed to get item from localStorage".to_string())
46        })? {
47            Some(json_str) => {
48                let value = serde_json::from_str(&json_str)
49                    .map_err(|e| StorageError::Serialization(e))?;
50                Ok(Some(value))
51            }
52            None => Ok(None),
53        }
54    }
55
56    /// Set a value in localStorage
57    fn set_to_local_storage<T: Serialize>(key: &str, value: &T) -> Result<(), StorageError> {
58        let storage = Self::get_local_storage()?;
59        
60        let json_str = serde_json::to_string(value)
61            .map_err(|e| StorageError::Serialization(e))?;
62        
63        storage.set_item(key, &json_str).map_err(|_| {
64            StorageError::OperationFailed("Failed to set item in localStorage".to_string())
65        })?;
66        
67        Ok(())
68    }
69
70    /// Remove a value from localStorage
71    fn remove_from_local_storage(key: &str) -> Result<(), StorageError> {
72        let storage = Self::get_local_storage()?;
73        
74        storage.remove_item(key).map_err(|_| {
75            StorageError::OperationFailed("Failed to remove item from localStorage".to_string())
76        })?;
77        
78        Ok(())
79    }
80
81    /// Get all keys from localStorage
82    fn get_keys_from_local_storage() -> Result<Vec<String>, StorageError> {
83        let storage = Self::get_local_storage()?;
84        let length = storage.length().map_err(|_| {
85            StorageError::OperationFailed("Failed to get localStorage length".to_string())
86        })?;
87        
88        let mut keys = Vec::new();
89        for i in 0..length {
90            if let Some(key) = storage.key(i).map_err(|_| {
91                StorageError::OperationFailed("Failed to get localStorage key".to_string())
92            })? {
93                keys.push(key);
94            }
95        }
96        
97        Ok(keys)
98    }
99}
100
101impl Clone for IndexedDbStorage {
102    fn clone(&self) -> Self {
103        Self {
104            db_name: self.db_name.clone(),
105            store_name: self.store_name.clone(),
106            fallback: self.fallback.clone(),
107        }
108    }
109}
110
111#[async_trait]
112impl LocalStorage for IndexedDbStorage {
113    async fn set<T: Serialize + Send + Sync>(&self, key: &str, value: &T) -> Result<(), StorageError> {
114        // For now, use localStorage fallback since IndexedDB is not fully implemented
115        match Self::set_to_local_storage(key, value) {
116            Ok(()) => Ok(()),
117            Err(_) => {
118                // Fall back to in-memory storage
119                self.fallback.set(key, value).await
120            }
121        }
122    }
123
124    async fn get<T: DeserializeOwned + Send + Sync>(&self, key: &str) -> Result<Option<T>, StorageError> {
125        // For now, use localStorage fallback since IndexedDB is not fully implemented
126        match Self::get_from_local_storage(key) {
127            Ok(value) => Ok(value),
128            Err(_) => {
129                // Fall back to in-memory storage
130                self.fallback.get(key).await
131            }
132        }
133    }
134
135    async fn remove(&self, key: &str) -> Result<(), StorageError> {
136        // For now, use localStorage fallback since IndexedDB is not fully implemented
137        match Self::remove_from_local_storage(key) {
138            Ok(()) => Ok(()),
139            Err(_) => {
140                // Fall back to in-memory storage
141                self.fallback.remove(key).await
142            }
143        }
144    }
145
146    async fn keys(&self) -> Result<Vec<String>, StorageError> {
147        // For now, use localStorage fallback since IndexedDB is not fully implemented
148        match Self::get_keys_from_local_storage() {
149            Ok(keys) => Ok(keys),
150            Err(_) => {
151                // Fall back to in-memory storage
152                self.fallback.keys().await
153            }
154        }
155    }
156
157    async fn contains_key(&self, key: &str) -> Result<bool, StorageError> {
158        // For now, use localStorage fallback since IndexedDB is not fully implemented
159        match Self::get_from_local_storage::<()>(key) {
160            Ok(Some(_)) => Ok(true),
161            Ok(None) => Ok(false),
162            Err(_) => {
163                // Fall back to in-memory storage
164                self.fallback.contains_key(key).await
165            }
166        }
167    }
168
169    async fn len(&self) -> Result<usize, StorageError> {
170        // For now, use localStorage fallback since IndexedDB is not fully implemented
171        match Self::get_keys_from_local_storage() {
172            Ok(keys) => Ok(keys.len()),
173            Err(_) => {
174                // Fall back to in-memory storage
175                self.fallback.len().await
176            }
177        }
178    }
179
180    async fn is_empty(&self) -> Result<bool, StorageError> {
181        // For now, use localStorage fallback since IndexedDB is not fully implemented
182        match Self::get_keys_from_local_storage() {
183            Ok(keys) => Ok(keys.is_empty()),
184            Err(_) => {
185                // Fall back to in-memory storage
186                self.fallback.is_empty().await
187            }
188        }
189    }
190
191    async fn clear(&self) -> Result<(), StorageError> {
192        // For now, use localStorage fallback since IndexedDB is not fully implemented
193        let storage = Self::get_local_storage()?;
194        storage.clear().map_err(|_| {
195            StorageError::OperationFailed("Failed to clear localStorage".to_string())
196        })?;
197        Ok(())
198    }
199}
200
201// IndexedDB-specific methods (not yet fully implemented)
202impl IndexedDbStorage {
203    /// Create an IndexedDB database
204    pub async fn create_database(&self) -> Result<(), StorageError> {
205        // TODO: Implement actual IndexedDB creation
206        Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
207    }
208
209    /// Open an IndexedDB database
210    pub async fn open_database(&self) -> Result<(), StorageError> {
211        // TODO: Implement actual IndexedDB opening
212        Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
213    }
214
215    /// Create an object store
216    pub async fn create_object_store(&self) -> Result<(), StorageError> {
217        // TODO: Implement actual object store creation
218        Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
219    }
220
221    /// Get a value from IndexedDB
222    pub async fn indexeddb_get<T: DeserializeOwned>(&self, _key: &str) -> Result<Option<T>, StorageError> {
223        // TODO: Implement actual IndexedDB get
224        Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
225    }
226
227    /// Set a value in IndexedDB
228    pub async fn indexeddb_set<T: Serialize>(&self, _key: &str, _value: &T) -> Result<(), StorageError> {
229        // TODO: Implement actual IndexedDB set
230        Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
231    }
232
233    /// Delete a value from IndexedDB
234    pub async fn indexeddb_delete(&self, _key: &str) -> Result<(), StorageError> {
235        // TODO: Implement actual IndexedDB delete
236        Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
237    }
238
239    /// Get all keys from IndexedDB
240    pub async fn indexeddb_keys(&self) -> Result<Vec<String>, StorageError> {
241        // TODO: Implement actual IndexedDB keys
242        Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[tokio::test]
251    async fn test_indexeddb_storage_fallback() {
252        let storage = IndexedDbStorage::new(
253            "test_db".to_string(),
254            "test_store".to_string(),
255        );
256
257        // Test that operations work (falling back to localStorage or memory)
258        assert!(storage.set("key1", &"value1".to_string()).await.is_ok());
259        
260        let value = storage.get::<String>("key1").await.unwrap();
261        // Note: This might be None if localStorage is not available in test environment
262        // The important thing is that it doesn't panic
263        
264        // Test remove
265        assert!(storage.remove("key1").await.is_ok());
266    }
267
268    #[tokio::test]
269    async fn test_indexeddb_storage_clone() {
270        let storage = IndexedDbStorage::new(
271            "test_db".to_string(),
272            "test_store".to_string(),
273        );
274        let cloned_storage = storage.clone();
275
276        // Test that cloned storage works
277        assert!(cloned_storage.set("key1", &"value1".to_string()).await.is_ok());
278    }
279}