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>(
28        &self,
29        key: &str,
30        value: &T,
31    ) -> Result<(), StorageError>;
32
33    /// Retrieve a value by key
34    async fn get<T: DeserializeOwned + Send + Sync>(
35        &self,
36        key: &str,
37    ) -> Result<Option<T>, StorageError>;
38
39    /// Remove a value by key
40    async fn remove(&self, key: &str) -> Result<(), StorageError>;
41
42    /// Get all keys
43    async fn keys(&self) -> Result<Vec<String>, StorageError>;
44
45    /// Check if a key exists
46    async fn contains_key(&self, key: &str) -> Result<bool, StorageError> {
47        let keys = self.keys().await?;
48        Ok(keys.contains(&key.to_string()))
49    }
50
51    /// Get the number of stored items
52    async fn len(&self) -> Result<usize, StorageError> {
53        let keys = self.keys().await?;
54        Ok(keys.len())
55    }
56
57    /// Check if storage is empty
58    async fn is_empty(&self) -> Result<bool, StorageError> {
59        self.len().await.map(|l| l == 0)
60    }
61
62    /// Clear all stored data
63    async fn clear(&self) -> Result<(), StorageError> {
64        let keys = self.keys().await?;
65        for key in keys {
66            self.remove(&key).await?;
67        }
68        Ok(())
69    }
70}
71
72/// Storage enum that can hold different storage backends
73#[derive(Clone)]
74pub enum Storage {
75    Memory(memory::MemoryStorage),
76    IndexedDb(indexeddb::IndexedDbStorage),
77}
78
79impl Storage {
80    pub fn memory() -> Self {
81        Self::Memory(memory::MemoryStorage::new())
82    }
83
84    pub async fn indexeddb(db_name: &str, version: u32) -> Result<Self, StorageError> {
85        let storage = indexeddb::IndexedDbStorage::new(db_name.to_string(), "default".to_string());
86        Ok(Self::IndexedDb(storage))
87    }
88
89    pub async fn indexeddb_default() -> Result<Self, StorageError> {
90        let storage =
91            indexeddb::IndexedDbStorage::new("default_db".to_string(), "default".to_string());
92        Ok(Self::IndexedDb(storage))
93    }
94}
95
96#[async_trait]
97impl LocalStorage for Storage {
98    async fn set<T: Serialize + Send + Sync>(
99        &self,
100        key: &str,
101        value: &T,
102    ) -> Result<(), StorageError> {
103        match self {
104            Storage::Memory(storage) => storage.set(key, value).await,
105            Storage::IndexedDb(storage) => storage.set(key, value).await,
106        }
107    }
108
109    async fn get<T: DeserializeOwned + Send + Sync>(
110        &self,
111        key: &str,
112    ) -> Result<Option<T>, StorageError> {
113        match self {
114            Storage::Memory(storage) => storage.get(key).await,
115            Storage::IndexedDb(storage) => storage.get(key).await,
116        }
117    }
118
119    async fn remove(&self, key: &str) -> Result<(), StorageError> {
120        match self {
121            Storage::Memory(storage) => storage.remove(key).await,
122            Storage::IndexedDb(storage) => storage.remove(key).await,
123        }
124    }
125
126    async fn keys(&self) -> Result<Vec<String>, StorageError> {
127        match self {
128            Storage::Memory(storage) => storage.keys().await,
129            Storage::IndexedDb(storage) => storage.keys().await,
130        }
131    }
132
133    async fn contains_key(&self, key: &str) -> Result<bool, StorageError> {
134        match self {
135            Storage::Memory(storage) => storage.contains_key(key).await,
136            Storage::IndexedDb(storage) => storage.contains_key(key).await,
137        }
138    }
139
140    async fn len(&self) -> Result<usize, StorageError> {
141        match self {
142            Storage::Memory(storage) => storage.len().await,
143            Storage::IndexedDb(storage) => storage.len().await,
144        }
145    }
146
147    async fn is_empty(&self) -> Result<bool, StorageError> {
148        match self {
149            Storage::Memory(storage) => storage.is_empty().await,
150            Storage::IndexedDb(storage) => storage.is_empty().await,
151        }
152    }
153
154    async fn clear(&self) -> Result<(), StorageError> {
155        match self {
156            Storage::Memory(storage) => storage.clear().await,
157            Storage::IndexedDb(storage) => storage.clear().await,
158        }
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[tokio::test]
167    async fn test_storage_enum() {
168        let storage = Storage::memory();
169
170        // Test basic operations
171        assert!(storage.set("key1", &"value1".to_string()).await.is_ok());
172        let value = storage.get::<String>("key1").await.unwrap();
173        assert_eq!(value, Some("value1".to_string()));
174
175        // Test remove
176        assert!(storage.remove("key1").await.is_ok());
177        let value = storage.get::<String>("key1").await.unwrap();
178        assert_eq!(value, None);
179    }
180}