leptos_sync_core/storage/
indexeddb.rs1use super::{LocalStorage, StorageError};
4use async_trait::async_trait;
5use serde::{de::DeserializeOwned, Serialize};
6use wasm_bindgen::JsCast;
7use web_sys::{window, Storage};
8
9pub 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 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 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 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 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 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 match Self::set_to_local_storage(key, value) {
116 Ok(()) => Ok(()),
117 Err(_) => {
118 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 match Self::get_from_local_storage(key) {
127 Ok(value) => Ok(value),
128 Err(_) => {
129 self.fallback.get(key).await
131 }
132 }
133 }
134
135 async fn remove(&self, key: &str) -> Result<(), StorageError> {
136 match Self::remove_from_local_storage(key) {
138 Ok(()) => Ok(()),
139 Err(_) => {
140 self.fallback.remove(key).await
142 }
143 }
144 }
145
146 async fn keys(&self) -> Result<Vec<String>, StorageError> {
147 match Self::get_keys_from_local_storage() {
149 Ok(keys) => Ok(keys),
150 Err(_) => {
151 self.fallback.keys().await
153 }
154 }
155 }
156
157 async fn contains_key(&self, key: &str) -> Result<bool, StorageError> {
158 match Self::get_from_local_storage::<()>(key) {
160 Ok(Some(_)) => Ok(true),
161 Ok(None) => Ok(false),
162 Err(_) => {
163 self.fallback.contains_key(key).await
165 }
166 }
167 }
168
169 async fn len(&self) -> Result<usize, StorageError> {
170 match Self::get_keys_from_local_storage() {
172 Ok(keys) => Ok(keys.len()),
173 Err(_) => {
174 self.fallback.len().await
176 }
177 }
178 }
179
180 async fn is_empty(&self) -> Result<bool, StorageError> {
181 match Self::get_keys_from_local_storage() {
183 Ok(keys) => Ok(keys.is_empty()),
184 Err(_) => {
185 self.fallback.is_empty().await
187 }
188 }
189 }
190
191 async fn clear(&self) -> Result<(), StorageError> {
192 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
201impl IndexedDbStorage {
203 pub async fn create_database(&self) -> Result<(), StorageError> {
205 Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
207 }
208
209 pub async fn open_database(&self) -> Result<(), StorageError> {
211 Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
213 }
214
215 pub async fn create_object_store(&self) -> Result<(), StorageError> {
217 Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
219 }
220
221 pub async fn indexeddb_get<T: DeserializeOwned>(&self, _key: &str) -> Result<Option<T>, StorageError> {
223 Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
225 }
226
227 pub async fn indexeddb_set<T: Serialize>(&self, _key: &str, _value: &T) -> Result<(), StorageError> {
229 Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
231 }
232
233 pub async fn indexeddb_delete(&self, _key: &str) -> Result<(), StorageError> {
235 Err(StorageError::Unsupported("IndexedDB not yet fully implemented".to_string()))
237 }
238
239 pub async fn indexeddb_keys(&self) -> Result<Vec<String>, StorageError> {
241 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 assert!(storage.set("key1", &"value1".to_string()).await.is_ok());
259
260 let value = storage.get::<String>("key1").await.unwrap();
261 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 assert!(cloned_storage.set("key1", &"value1".to_string()).await.is_ok());
278 }
279}