use anyhow::{Context, Result};
/// RedisCore struct to manage Redis connections and commands.
pub struct RedisCore {
pub client: redis::Client,
}
impl RedisCore {
/// Initializes a new RedisCore instance.
pub fn new(redis_url: &str) -> Result<Self> {
let client = redis::Client::open(redis_url)
.context("Failed to create Redis client")?;
Ok(RedisCore { client })
}
/// Проверяет подключение к Redis командой PING
pub async fn ping(&self) -> Result<()> {
let mut conn = self.get_conn().await
.context("❌ ОШИБКА: Redis сервер не запущен или недоступен!")?;
let pong: String = redis::cmd("PING")
.query_async(&mut conn)
.await
.context("❌ ОШИБКА: Redis не отвечает на PING!")?;
if pong != "PONG" {
anyhow::bail!("❌ ОШИБКА: Redis вернул неверный ответ: {}", pong);
}
Ok(())
}
/// Удаляет все ключи с заданным префиксом (например, "hb:*")
pub async fn delete_by_prefix(&self, prefix: &str) -> Result<usize> {
let mut conn = self.get_conn().await
.context("❌ ОШИБКА: Не удалось подключиться к Redis для удаления!")?;
// Используем SCAN для безопасного поиска ключей
let mut keys: Vec<String> = Vec::new();
let mut cursor: usize = 0;
loop {
let (next_cursor, batch): (usize, Vec<String>) = redis::cmd("SCAN")
.arg(cursor)
.arg("MATCH")
.arg(prefix)
.arg("COUNT")
.arg(100)
.query_async(&mut conn)
.await
.context("❌ ОШИБКА: Не удалось выполнить SCAN!")?;
keys.extend(batch);
cursor = next_cursor;
if cursor == 0 {
break;
}
}
if !keys.is_empty() {
let deleted: usize = redis::cmd("DEL")
.arg(keys.as_slice())
.query_async(&mut conn)
.await
.context("❌ ОШИБКА: Не удалось выполнить DEL!")?;
Ok(deleted)
} else {
Ok(0)
}
}
/// Asynchronously retrieves a Redis connection.
pub async fn get_conn(&self) -> Result<redis::aio::MultiplexedConnection> {
let conn = self.client.get_multiplexed_async_connection().await
.context("Failed to get Redis connection")?;
Ok(conn)
}
}
/// Helper function to get a connection from RedisCore.
pub async fn get_redis_conn(redis: &RedisCore) -> Result<redis::aio::MultiplexedConnection> {
redis.get_conn().await
}