Skip to main content

rpytest_core/storage/
sled_backend.rs

1//! Sled-based storage backend implementation.
2
3use std::path::Path;
4
5use sled::Db;
6
7use super::traits::{StorageBackend, StorageError, StorageResult};
8
9/// Sled-based storage backend.
10pub struct SledBackend {
11    db: Db,
12}
13
14impl StorageBackend for SledBackend {
15    fn open(path: &Path) -> StorageResult<Self>
16    where
17        Self: Sized,
18    {
19        let db = sled::open(path).map_err(|e| StorageError::Backend(e.to_string()))?;
20        Ok(Self { db })
21    }
22
23    fn get(&self, key: &[u8]) -> StorageResult<Option<Vec<u8>>> {
24        self.db
25            .get(key)
26            .map(|opt| opt.map(|v| v.to_vec()))
27            .map_err(|e| StorageError::Backend(e.to_string()))
28    }
29
30    fn set(&self, key: &[u8], value: &[u8]) -> StorageResult<()> {
31        self.db
32            .insert(key, value)
33            .map_err(|e| StorageError::Backend(e.to_string()))?;
34        Ok(())
35    }
36
37    fn delete(&self, key: &[u8]) -> StorageResult<()> {
38        self.db
39            .remove(key)
40            .map_err(|e| StorageError::Backend(e.to_string()))?;
41        Ok(())
42    }
43
44    fn flush(&self) -> StorageResult<()> {
45        self.db
46            .flush()
47            .map_err(|e| StorageError::Backend(e.to_string()))?;
48        Ok(())
49    }
50
51    fn scan_prefix(&self, prefix: &[u8]) -> StorageResult<Vec<(Vec<u8>, Vec<u8>)>> {
52        let results: Result<Vec<_>, _> = self
53            .db
54            .scan_prefix(prefix)
55            .map(|res| res.map(|(k, v)| (k.to_vec(), v.to_vec())))
56            .collect();
57
58        results.map_err(|e| StorageError::Backend(e.to_string()))
59    }
60
61    fn clear(&self) -> StorageResult<()> {
62        self.db
63            .clear()
64            .map_err(|e| StorageError::Backend(e.to_string()))?;
65        Ok(())
66    }
67}
68
69impl SledBackend {
70    /// Get the underlying sled database for advanced operations.
71    pub fn inner(&self) -> &Db {
72        &self.db
73    }
74
75    /// Check if the database is empty.
76    pub fn is_empty(&self) -> StorageResult<bool> {
77        Ok(self.db.is_empty())
78    }
79
80    /// Get the number of entries in the database.
81    pub fn len(&self) -> StorageResult<usize> {
82        Ok(self.db.len())
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use tempfile::TempDir;
90
91    fn create_test_db() -> (SledBackend, TempDir) {
92        let tmp = TempDir::new().unwrap();
93        let backend = SledBackend::open(tmp.path()).unwrap();
94        (backend, tmp)
95    }
96
97    #[test]
98    fn test_basic_operations() {
99        let (db, _tmp) = create_test_db();
100
101        // Set and get
102        db.set(b"key1", b"value1").unwrap();
103        assert_eq!(db.get(b"key1").unwrap(), Some(b"value1".to_vec()));
104
105        // Contains
106        assert!(db.contains(b"key1").unwrap());
107        assert!(!db.contains(b"nonexistent").unwrap());
108
109        // Delete
110        db.delete(b"key1").unwrap();
111        assert_eq!(db.get(b"key1").unwrap(), None);
112    }
113
114    #[test]
115    fn test_scan_prefix() {
116        let (db, _tmp) = create_test_db();
117
118        db.set(b"inv:test1", b"data1").unwrap();
119        db.set(b"inv:test2", b"data2").unwrap();
120        db.set(b"dur:test1", b"100").unwrap();
121
122        let inv_results = db.scan_prefix(b"inv:").unwrap();
123        assert_eq!(inv_results.len(), 2);
124
125        let dur_results = db.scan_prefix(b"dur:").unwrap();
126        assert_eq!(dur_results.len(), 1);
127    }
128
129    #[test]
130    fn test_clear() {
131        let (db, _tmp) = create_test_db();
132
133        db.set(b"key1", b"value1").unwrap();
134        db.set(b"key2", b"value2").unwrap();
135        assert!(!db.is_empty().unwrap());
136
137        db.clear().unwrap();
138        assert!(db.is_empty().unwrap());
139    }
140
141    #[test]
142    fn test_persistence() {
143        let tmp = TempDir::new().unwrap();
144
145        // Write data
146        {
147            let db = SledBackend::open(tmp.path()).unwrap();
148            db.set(b"persistent", b"data").unwrap();
149            db.flush().unwrap();
150        }
151
152        // Read data in new instance
153        {
154            let db = SledBackend::open(tmp.path()).unwrap();
155            assert_eq!(db.get(b"persistent").unwrap(), Some(b"data".to_vec()));
156        }
157    }
158}