Skip to main content

chaincraft_rust/
storage.rs

1//! Storage implementation for chain data
2
3use crate::error::{Result, StorageError};
4use async_trait::async_trait;
5use std::collections::HashMap;
6use std::path::Path;
7
8#[cfg(feature = "persistent")]
9use sled;
10
11/// Trait for key-value storage backends
12#[async_trait]
13pub trait Storage: Send + Sync {
14    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>>;
15    async fn put(&self, key: &str, value: Vec<u8>) -> Result<()>;
16    async fn delete(&self, key: &str) -> Result<()>;
17    async fn exists(&self, key: &str) -> Result<bool>;
18    async fn clear(&self) -> Result<()>;
19    async fn initialize(&self) -> Result<()>;
20    /// Return the number of stored keys (best-effort for non-in-memory backends).
21    async fn len(&self) -> Result<usize>;
22}
23
24/// In-memory storage implementation
25#[derive(Debug, Default)]
26pub struct MemoryStorage {
27    data: tokio::sync::RwLock<HashMap<String, Vec<u8>>>,
28}
29
30impl MemoryStorage {
31    pub fn new() -> Self {
32        Self::default()
33    }
34}
35
36#[async_trait]
37impl Storage for MemoryStorage {
38    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
39        let data = self.data.read().await;
40        Ok(data.get(key).cloned())
41    }
42
43    async fn put(&self, key: &str, value: Vec<u8>) -> Result<()> {
44        let mut data = self.data.write().await;
45        data.insert(key.to_string(), value);
46        Ok(())
47    }
48
49    async fn delete(&self, key: &str) -> Result<()> {
50        let mut data = self.data.write().await;
51        data.remove(key);
52        Ok(())
53    }
54
55    async fn exists(&self, key: &str) -> Result<bool> {
56        let data = self.data.read().await;
57        Ok(data.contains_key(key))
58    }
59
60    async fn clear(&self) -> Result<()> {
61        let mut data = self.data.write().await;
62        data.clear();
63        Ok(())
64    }
65
66    async fn initialize(&self) -> Result<()> {
67        // In-memory storage doesn't need initialization
68        Ok(())
69    }
70
71    async fn len(&self) -> Result<usize> {
72        let data = self.data.read().await;
73        Ok(data.len())
74    }
75}
76
77/// On-disk storage implementation using `sled`.
78///
79/// This roughly corresponds to the Python version's dbm-based persistent storage.
80#[cfg(feature = "persistent")]
81#[derive(Debug)]
82pub struct SledStorage {
83    db: sled::Db,
84}
85
86#[cfg(feature = "persistent")]
87impl SledStorage {
88    /// Open or create a sled database at the given path.
89    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
90        let db = sled::open(path).map_err(|e| {
91            StorageError::DatabaseOperation {
92                reason: e.to_string(),
93            }
94        })?;
95        Ok(Self { db })
96    }
97}
98
99#[cfg(feature = "persistent")]
100#[async_trait]
101impl Storage for SledStorage {
102    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
103        let res = self
104            .db
105            .get(key.as_bytes())
106            .map_err(|e| StorageError::DatabaseOperation {
107                reason: e.to_string(),
108            })?;
109        Ok(res.map(|ivec| ivec.to_vec()))
110    }
111
112    async fn put(&self, key: &str, value: Vec<u8>) -> Result<()> {
113        self.db
114            .insert(key.as_bytes(), value)
115            .map_err(|e| StorageError::DatabaseOperation {
116                reason: e.to_string(),
117            })?;
118        self.db
119            .flush()
120            .map_err(|e| StorageError::DatabaseOperation {
121                reason: e.to_string(),
122            })?;
123        Ok(())
124    }
125
126    async fn delete(&self, key: &str) -> Result<()> {
127        self.db
128            .remove(key.as_bytes())
129            .map_err(|e| StorageError::DatabaseOperation {
130                reason: e.to_string(),
131            })?;
132        self.db
133            .flush()
134            .map_err(|e| StorageError::DatabaseOperation {
135                reason: e.to_string(),
136            })?;
137        Ok(())
138    }
139
140    async fn exists(&self, key: &str) -> Result<bool> {
141        let res = self
142            .db
143            .contains_key(key.as_bytes())
144            .map_err(|e| StorageError::DatabaseOperation {
145                reason: e.to_string(),
146            })?;
147        Ok(res)
148    }
149
150    async fn clear(&self) -> Result<()> {
151        self.db
152            .clear()
153            .map_err(|e| StorageError::DatabaseOperation {
154                reason: e.to_string(),
155            })?;
156        self.db
157            .flush()
158            .map_err(|e| StorageError::DatabaseOperation {
159                reason: e.to_string(),
160            })?;
161        Ok(())
162    }
163
164    async fn initialize(&self) -> Result<()> {
165        // sled opens lazily; nothing special required
166        Ok(())
167    }
168
169    async fn len(&self) -> Result<usize> {
170        Ok(self.db.len())
171    }
172}