use crate::error::Result;
use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
#[async_trait]
pub trait RedisNativeOps: Send + Sync {
async fn set(&self, key: &str, value: &[u8], ttl: Option<u64>) -> Result<()>;
async fn get_bytes(&self, key: &str) -> Result<Option<Vec<u8>>>;
async fn increment(&self, key: &str, amount: i64, ttl: Option<u64>) -> Result<i64>;
async fn decrement(&self, key: &str, amount: i64, ttl: Option<u64>) -> Result<i64>;
async fn get_counter(&self, key: &str) -> Result<Option<i64>>;
async fn zadd(&self, key: &str, score: f64, member: &str, ttl: Option<u64>) -> Result<u64>;
async fn zrange_by_score(
&self,
key: &str,
min: f64,
max: f64,
with_scores: bool,
) -> Result<Vec<ZSetMember>>;
async fn zscore(&self, key: &str, member: &str) -> Result<Option<f64>>;
async fn zrem(&self, key: &str, members: &[&str]) -> Result<u64>;
async fn zcard(&self, key: &str) -> Result<u64>;
async fn scan_keys(&self, pattern: &str, count: usize) -> Result<Vec<String>>;
fn scan_iter(&self, pattern: &str) -> ScanKeyIterator;
async fn eval_readonly(&self, script: &str, keys: &[&str], args: &[&str]) -> Result<String>;
async fn eval_write(&self, script: &str, keys: &[&str], args: &[&str]) -> Result<String>;
async fn evalsha(&self, sha1: &str, keys: &[&str], args: &[&str]) -> Result<String>;
async fn script_load(&self, script: &str) -> Result<String>;
async fn script_exists(&self, sha1: &[&str]) -> Result<Vec<bool>>;
async fn get_many(&self, keys: &[&str]) -> Result<HashMap<String, Vec<u8>>>;
async fn set_many(&self, items: HashMap<&str, &[u8]>, ttl: Option<u64>) -> Result<()>;
async fn del_pattern(&self, pattern: &str) -> Result<u64>;
}
#[derive(Debug, Clone)]
pub struct ZSetMember {
pub member: String,
pub score: f64,
}
#[allow(dead_code)]
pub struct ScanKeyIterator {
client: Arc<dyn RedisNativeOps>,
pattern: String,
cursor: u64,
finished: bool,
}
impl ScanKeyIterator {
pub fn new(client: Arc<dyn RedisNativeOps>, pattern: &str) -> Self {
Self {
client,
pattern: pattern.to_string(),
cursor: 0,
finished: false,
}
}
}
impl Iterator for ScanKeyIterator {
type Item = String;
fn next(&mut self) -> Option<String> {
None
}
}
impl ScanKeyIterator {
pub async fn async_next(&mut self) -> Option<String> {
if self.finished {
return None;
}
self.finished = true;
None
}
pub async fn collect_all(&mut self) -> crate::error::Result<Vec<String>> {
let mut all_keys = Vec::new();
while let Some(key) = self.async_next().await {
all_keys.push(key);
}
Ok(all_keys)
}
}
#[derive(Debug, Clone)]
pub struct ScriptCache {
scripts: Arc<std::sync::Mutex<HashMap<String, String>>>,
}
impl ScriptCache {
pub fn new() -> Self {
Self {
scripts: Arc::new(std::sync::Mutex::new(HashMap::new())),
}
}
pub fn get_sha(&self, script: &str) -> Option<String> {
self.scripts
.lock()
.expect("ScriptCache lock poisoned")
.get(script)
.cloned()
}
pub fn cache(&self, script: &str, sha: &str) {
self.scripts
.lock()
.expect("ScriptCache lock poisoned")
.insert(script.to_string(), sha.to_string());
}
pub fn contains(&self, script: &str) -> bool {
self.scripts
.lock()
.expect("ScriptCache lock poisoned")
.contains_key(script)
}
pub fn clear(&self) {
self.scripts
.lock()
.expect("ScriptCache lock poisoned")
.clear();
}
}
impl Default for ScriptCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zset_member() {
let member = ZSetMember {
member: "test".to_string(),
score: 1.5,
};
assert_eq!(member.member, "test");
assert_eq!(member.score, 1.5);
}
#[test]
fn test_script_cache() {
let cache = ScriptCache::new();
assert!(cache.get_sha("test").is_none());
cache.cache("test script", "abc123");
assert_eq!(cache.get_sha("test script"), Some("abc123".to_string()));
assert!(cache.contains("test script"));
assert!(!cache.contains("other script"));
cache.clear();
assert!(cache.get_sha("test script").is_none());
}
#[test]
fn test_script_cache_default() {
let cache = ScriptCache::default();
assert!(cache.get_sha("test").is_none());
}
}