use async_trait::async_trait;
use crate::error::StateResult;
#[async_trait]
pub trait StateBackend: Send + Sync {
async fn get(&self, key: &[u8]) -> StateResult<Option<Vec<u8>>>;
async fn put(&self, key: &[u8], value: &[u8]) -> StateResult<()>;
async fn delete(&self, key: &[u8]) -> StateResult<()>;
async fn list_keys(&self, prefix: &[u8]) -> StateResult<Vec<Vec<u8>>>;
async fn clear(&self) -> StateResult<()> {
let keys = self.list_keys(b"").await?;
for key in keys {
self.delete(&key).await?;
}
Ok(())
}
async fn count(&self) -> StateResult<usize> {
Ok(self.list_keys(b"").await?.len())
}
async fn contains(&self, key: &[u8]) -> StateResult<bool> {
Ok(self.get(key).await?.is_some())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
fn test_key(id: u32) -> Vec<u8> {
format!("test:{}", id).into_bytes()
}
pub async fn test_backend_basic_ops<B: StateBackend>(backend: B) {
backend.put(&test_key(1), b"value1").await.unwrap();
let value = backend.get(&test_key(1)).await.unwrap();
assert_eq!(value, Some(b"value1".to_vec()));
let value = backend.get(&test_key(999)).await.unwrap();
assert_eq!(value, None);
backend.put(&test_key(1), b"new_value").await.unwrap();
let value = backend.get(&test_key(1)).await.unwrap();
assert_eq!(value, Some(b"new_value".to_vec()));
backend.delete(&test_key(1)).await.unwrap();
let value = backend.get(&test_key(1)).await.unwrap();
assert_eq!(value, None);
backend.delete(&test_key(999)).await.unwrap();
}
pub async fn test_backend_list_keys<B: StateBackend>(backend: B) {
backend.clear().await.unwrap();
backend.put(b"window:1", b"w1").await.unwrap();
backend.put(b"window:2", b"w2").await.unwrap();
backend.put(b"window:3", b"w3").await.unwrap();
backend.put(b"meta:1", b"m1").await.unwrap();
backend.put(b"meta:2", b"m2").await.unwrap();
let window_keys = backend.list_keys(b"window:").await.unwrap();
assert_eq!(window_keys.len(), 3);
let meta_keys = backend.list_keys(b"meta:").await.unwrap();
assert_eq!(meta_keys.len(), 2);
let all_keys = backend.list_keys(b"").await.unwrap();
assert_eq!(all_keys.len(), 5);
}
pub async fn test_backend_clear<B: StateBackend>(backend: B) {
backend.put(b"key1", b"val1").await.unwrap();
backend.put(b"key2", b"val2").await.unwrap();
backend.put(b"key3", b"val3").await.unwrap();
assert_eq!(backend.count().await.unwrap(), 3);
backend.clear().await.unwrap();
assert_eq!(backend.count().await.unwrap(), 0);
}
pub async fn test_backend_contains<B: StateBackend>(backend: B) {
backend.clear().await.unwrap();
assert!(!backend.contains(b"test").await.unwrap());
backend.put(b"test", b"value").await.unwrap();
assert!(backend.contains(b"test").await.unwrap());
backend.delete(b"test").await.unwrap();
assert!(!backend.contains(b"test").await.unwrap());
}
}