leptos_sync_core/storage/
mod.rs

1//! Storage layer for local-first applications
2
3use async_trait::async_trait;
4use serde::{de::DeserializeOwned, Serialize};
5use thiserror::Error;
6
7pub mod indexed;
8pub mod indexeddb;
9pub mod memory;
10
11#[derive(Error, Debug)]
12pub enum StorageError {
13    #[error("Key not found: {0}")]
14    NotFound(String),
15    #[error("Serialization error: {0}")]
16    Serialization(#[from] serde_json::Error),
17    #[error("Storage operation failed: {0}")]
18    OperationFailed(String),
19    #[error("Unsupported operation: {0}")]
20    Unsupported(String),
21}
22
23/// Trait for local storage implementations
24#[async_trait]
25pub trait LocalStorage: Send + Sync {
26    /// Store a value with the given key
27    async fn set<T: Serialize + Send + Sync>(&self, key: &str, value: &T) -> Result<(), StorageError>;
28    
29    /// Retrieve a value by key
30    async fn get<T: DeserializeOwned + Send + Sync>(&self, key: &str) -> Result<Option<T>, StorageError>;
31    
32    /// Remove a value by key
33    async fn remove(&self, key: &str) -> Result<(), StorageError>;
34    
35    /// Get all keys
36    async fn keys(&self) -> Result<Vec<String>, StorageError>;
37    
38    /// Check if a key exists
39    async fn contains_key(&self, key: &str) -> Result<bool, StorageError> {
40        let keys = self.keys().await?;
41        Ok(keys.contains(&key.to_string()))
42    }
43    
44    /// Get the number of stored items
45    async fn len(&self) -> Result<usize, StorageError> {
46        let keys = self.keys().await?;
47        Ok(keys.len())
48    }
49    
50    /// Check if storage is empty
51    async fn is_empty(&self) -> Result<bool, StorageError> {
52        self.len().await.map(|l| l == 0)
53    }
54    
55    /// Clear all stored data
56    async fn clear(&self) -> Result<(), StorageError> {
57        let keys = self.keys().await?;
58        for key in keys {
59            self.remove(&key).await?;
60        }
61        Ok(())
62    }
63}
64
65/// Storage enum that can hold different storage backends
66#[derive(Clone)]
67pub enum Storage {
68    Memory(memory::MemoryStorage),
69    IndexedDb(indexeddb::IndexedDbStorage),
70}
71
72impl Storage {
73    pub fn memory() -> Self {
74        Self::Memory(memory::MemoryStorage::new())
75    }
76    
77    pub fn indexeddb(db_name: String, store_name: String) -> Self {
78        Self::IndexedDb(indexeddb::IndexedDbStorage::new(db_name, store_name))
79    }
80}
81
82#[async_trait]
83impl LocalStorage for Storage {
84    async fn set<T: Serialize + Send + Sync>(&self, key: &str, value: &T) -> Result<(), StorageError> {
85        match self {
86            Storage::Memory(storage) => storage.set(key, value).await,
87            Storage::IndexedDb(storage) => storage.set(key, value).await,
88        }
89    }
90    
91    async fn get<T: DeserializeOwned + Send + Sync>(&self, key: &str) -> Result<Option<T>, StorageError> {
92        match self {
93            Storage::Memory(storage) => storage.get(key).await,
94            Storage::IndexedDb(storage) => storage.get(key).await,
95        }
96    }
97    
98    async fn remove(&self, key: &str) -> Result<(), StorageError> {
99        match self {
100            Storage::Memory(storage) => storage.remove(key).await,
101            Storage::IndexedDb(storage) => storage.remove(key).await,
102        }
103    }
104    
105    async fn keys(&self) -> Result<Vec<String>, StorageError> {
106        match self {
107            Storage::Memory(storage) => storage.keys().await,
108            Storage::IndexedDb(storage) => storage.keys().await,
109        }
110    }
111    
112    async fn contains_key(&self, key: &str) -> Result<bool, StorageError> {
113        match self {
114            Storage::Memory(storage) => storage.contains_key(key).await,
115            Storage::IndexedDb(storage) => storage.contains_key(key).await,
116        }
117    }
118    
119    async fn len(&self) -> Result<usize, StorageError> {
120        match self {
121            Storage::Memory(storage) => storage.len().await,
122            Storage::IndexedDb(storage) => storage.len().await,
123        }
124    }
125    
126    async fn is_empty(&self) -> Result<bool, StorageError> {
127        match self {
128            Storage::Memory(storage) => storage.is_empty().await,
129            Storage::IndexedDb(storage) => storage.is_empty().await,
130        }
131    }
132    
133    async fn clear(&self) -> Result<(), StorageError> {
134        match self {
135            Storage::Memory(storage) => storage.clear().await,
136            Storage::IndexedDb(storage) => storage.clear().await,
137        }
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    
145    #[tokio::test]
146    async fn test_storage_enum() {
147        let storage = Storage::memory();
148        
149        // Test basic operations
150        assert!(storage.set("key1", &"value1".to_string()).await.is_ok());
151        let value = storage.get::<String>("key1").await.unwrap();
152        assert_eq!(value, Some("value1".to_string()));
153        
154        // Test remove
155        assert!(storage.remove("key1").await.is_ok());
156        let value = storage.get::<String>("key1").await.unwrap();
157        assert_eq!(value, None);
158    }
159}