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