use async_trait::async_trait;
use std::time::Duration;
use thiserror::Error;
pub type StorageResult<T> = Result<T, StorageError>;
#[derive(Debug, Error)]
pub enum StorageError {
#[error("Storage operation failed: {0}")]
OperationFailed(String),
#[error("Key not found: {0}")]
KeyNotFound(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Connection error: {0}")]
ConnectionError(String),
#[error("Internal error: {0}")]
InternalError(String),
#[error("Unsupported operation '{0}' on this storage backend")]
Unsupported(&'static str),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScanPage {
pub keys: Vec<String>,
pub next_cursor: u64,
}
#[async_trait]
pub trait SaStorage: Send + Sync {
async fn get(&self, key: &str) -> StorageResult<Option<String>>;
async fn set(&self, key: &str, value: &str, ttl: Option<Duration>) -> StorageResult<()>;
async fn delete(&self, key: &str) -> StorageResult<()>;
async fn exists(&self, key: &str) -> StorageResult<bool>;
async fn expire(&self, key: &str, ttl: Duration) -> StorageResult<()>;
async fn ttl(&self, key: &str) -> StorageResult<Option<Duration>>;
async fn mget(&self, keys: &[&str]) -> StorageResult<Vec<Option<String>>> {
let mut results = Vec::with_capacity(keys.len());
for key in keys {
results.push(self.get(key).await?);
}
Ok(results)
}
async fn mset(&self, items: &[(&str, &str)], ttl: Option<Duration>) -> StorageResult<()> {
for (key, value) in items {
self.set(key, value, ttl).await?;
}
Ok(())
}
async fn mdel(&self, keys: &[&str]) -> StorageResult<()> {
for key in keys {
self.delete(key).await?;
}
Ok(())
}
async fn incr(&self, key: &str) -> StorageResult<i64> {
let current = self
.get(key)
.await?
.and_then(|v| v.parse::<i64>().ok())
.unwrap_or(0);
let new_value = current + 1;
self.set(key, &new_value.to_string(), None).await?;
Ok(new_value)
}
async fn decr(&self, key: &str) -> StorageResult<i64> {
let current = self
.get(key)
.await?
.and_then(|v| v.parse::<i64>().ok())
.unwrap_or(0);
let new_value = current - 1;
self.set(key, &new_value.to_string(), None).await?;
Ok(new_value)
}
async fn clear(&self) -> StorageResult<()>;
async fn set_if_absent(
&self,
key: &str,
value: &str,
ttl: Option<Duration>,
) -> StorageResult<bool>;
async fn get_del(&self, key: &str) -> StorageResult<Option<String>>;
async fn compare_and_swap(
&self,
key: &str,
expected: Option<&str>,
new_value: &str,
ttl: Option<Duration>,
) -> StorageResult<bool>;
async fn compare_and_delete(&self, key: &str, expected: &str) -> StorageResult<bool>;
async fn list_push(
&self,
key: &str,
member: &str,
unique: bool,
ttl: Option<Duration>,
) -> StorageResult<usize>;
async fn list_remove(&self, key: &str, member: &str) -> StorageResult<bool>;
async fn list_range(
&self,
key: &str,
start: usize,
limit: Option<usize>,
) -> StorageResult<Vec<String>>;
async fn list_len(&self, key: &str) -> StorageResult<usize>;
async fn scan(&self, pattern: &str, cursor: u64, limit: usize) -> StorageResult<ScanPage>;
}
pub async fn scan_all_keys(
storage: &dyn SaStorage,
pattern: &str,
page_size: usize,
) -> StorageResult<Vec<String>> {
let mut cursor = 0u64;
let mut all = Vec::new();
loop {
let page = storage.scan(pattern, cursor, page_size).await?;
all.extend(page.keys);
if page.next_cursor == 0 {
break;
}
cursor = page.next_cursor;
}
Ok(all)
}
pub async fn scan_all_keys_dedup(
storage: &dyn SaStorage,
pattern: &str,
page_size: usize,
) -> StorageResult<Vec<String>> {
use std::collections::HashSet;
let all = scan_all_keys(storage, pattern, page_size).await?;
let deduped: Vec<String> = all
.into_iter()
.collect::<HashSet<_>>()
.into_iter()
.collect();
Ok(deduped)
}