use std::path::Path;
use sled::Db;
use super::traits::{StorageBackend, StorageError, StorageResult};
pub struct SledBackend {
db: Db,
}
impl StorageBackend for SledBackend {
fn open(path: &Path) -> StorageResult<Self>
where
Self: Sized,
{
let db = sled::open(path).map_err(|e| StorageError::Backend(e.to_string()))?;
Ok(Self { db })
}
fn get(&self, key: &[u8]) -> StorageResult<Option<Vec<u8>>> {
self.db
.get(key)
.map(|opt| opt.map(|v| v.to_vec()))
.map_err(|e| StorageError::Backend(e.to_string()))
}
fn set(&self, key: &[u8], value: &[u8]) -> StorageResult<()> {
self.db
.insert(key, value)
.map_err(|e| StorageError::Backend(e.to_string()))?;
Ok(())
}
fn delete(&self, key: &[u8]) -> StorageResult<()> {
self.db
.remove(key)
.map_err(|e| StorageError::Backend(e.to_string()))?;
Ok(())
}
fn flush(&self) -> StorageResult<()> {
self.db
.flush()
.map_err(|e| StorageError::Backend(e.to_string()))?;
Ok(())
}
fn scan_prefix(&self, prefix: &[u8]) -> StorageResult<Vec<(Vec<u8>, Vec<u8>)>> {
let results: Result<Vec<_>, _> = self
.db
.scan_prefix(prefix)
.map(|res| res.map(|(k, v)| (k.to_vec(), v.to_vec())))
.collect();
results.map_err(|e| StorageError::Backend(e.to_string()))
}
fn clear(&self) -> StorageResult<()> {
self.db
.clear()
.map_err(|e| StorageError::Backend(e.to_string()))?;
Ok(())
}
}
impl SledBackend {
pub fn inner(&self) -> &Db {
&self.db
}
pub fn is_empty(&self) -> StorageResult<bool> {
Ok(self.db.is_empty())
}
pub fn len(&self) -> StorageResult<usize> {
Ok(self.db.len())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
fn create_test_db() -> (SledBackend, TempDir) {
let tmp = TempDir::new().unwrap();
let backend = SledBackend::open(tmp.path()).unwrap();
(backend, tmp)
}
#[test]
fn test_basic_operations() {
let (db, _tmp) = create_test_db();
db.set(b"key1", b"value1").unwrap();
assert_eq!(db.get(b"key1").unwrap(), Some(b"value1".to_vec()));
assert!(db.contains(b"key1").unwrap());
assert!(!db.contains(b"nonexistent").unwrap());
db.delete(b"key1").unwrap();
assert_eq!(db.get(b"key1").unwrap(), None);
}
#[test]
fn test_scan_prefix() {
let (db, _tmp) = create_test_db();
db.set(b"inv:test1", b"data1").unwrap();
db.set(b"inv:test2", b"data2").unwrap();
db.set(b"dur:test1", b"100").unwrap();
let inv_results = db.scan_prefix(b"inv:").unwrap();
assert_eq!(inv_results.len(), 2);
let dur_results = db.scan_prefix(b"dur:").unwrap();
assert_eq!(dur_results.len(), 1);
}
#[test]
fn test_clear() {
let (db, _tmp) = create_test_db();
db.set(b"key1", b"value1").unwrap();
db.set(b"key2", b"value2").unwrap();
assert!(!db.is_empty().unwrap());
db.clear().unwrap();
assert!(db.is_empty().unwrap());
}
#[test]
fn test_persistence() {
let tmp = TempDir::new().unwrap();
{
let db = SledBackend::open(tmp.path()).unwrap();
db.set(b"persistent", b"data").unwrap();
db.flush().unwrap();
}
{
let db = SledBackend::open(tmp.path()).unwrap();
assert_eq!(db.get(b"persistent").unwrap(), Some(b"data".to_vec()));
}
}
}