use deadpool::managed::Object;
use redis::{
Client, FromRedisValue, RedisError, RedisResult, ToRedisArgs,
aio::{ConnectionManager, ConnectionManagerConfig},
cluster::ClusterClientBuilder,
cmd,
};
use std::fmt::Display;
use crate::connection::RedisConnection;
pub struct Redis {
pub connection: RedisConnection,
pub db_name: String,
}
impl Redis {
pub async fn init(uri: &str, db_name: &str) -> RedisResult<Redis> {
let nodes = uri.split(",").collect::<Vec<&str>>();
if nodes.len() == 1 {
let client = Client::open(uri)?;
let config = ConnectionManagerConfig::default().set_number_of_retries(usize::MAX);
let connection = ConnectionManager::new_with_config(client.clone(), config).await?;
let connection = RedisConnection::Single(connection);
Ok(Redis {
connection,
db_name: db_name.to_string(),
})
} else {
let client = ClusterClientBuilder::new(nodes)
.use_protocol(redis::ProtocolVersion::RESP3)
.retries(u32::MAX)
.build()?;
let connection = client.get_async_connection().await?;
let connection = RedisConnection::Cluster(connection);
Ok(Redis {
connection,
db_name: db_name.to_string(),
})
}
}
}
#[derive(Debug)]
pub struct Manager {
uri: String,
db_name: String,
}
impl deadpool::managed::Manager for Manager {
type Type = Redis;
type Error = RedisError;
async fn create(&self) -> Result<Redis, RedisError> {
Redis::init(&self.uri, &self.db_name).await
}
async fn recycle(
&self,
_: &mut Redis,
_: &deadpool::managed::Metrics,
) -> deadpool::managed::RecycleResult<RedisError> {
Ok(())
}
}
pub struct RedisPool(pub deadpool::managed::Pool<Manager>);
impl RedisPool {
pub fn init(uri: &str, db_name: &str) -> anyhow::Result<Self> {
let manager = Manager {
uri: uri.to_string(),
db_name: db_name.to_string(),
};
let pool = deadpool::managed::Pool::<Manager>::builder(manager).build()?;
Ok(Self(pool))
}
pub async fn instance(&self) -> anyhow::Result<Object<Manager>> {
let redis = self.0.get().await?;
Ok(redis)
}
pub async fn set<K, V>(&self, key: K, value: V) -> anyhow::Result<()>
where
K: ToRedisArgs + Display,
V: ToRedisArgs,
{
let mut redis = self.instance().await?;
let key = format!("{}:{}", redis.db_name, key);
redis
.connection
.exec(cmd("SET").arg(key).arg(value))
.await?;
Ok(())
}
pub async fn set_ex<K, V>(&self, key: K, value: V, ex: u32) -> anyhow::Result<()>
where
K: ToRedisArgs + Display,
V: ToRedisArgs,
{
let mut redis = self.instance().await?;
let key = format!("{}:{}", redis.db_name, key);
redis
.connection
.exec(cmd("SET").arg(key).arg(value).arg("EX").arg(ex))
.await?;
Ok(())
}
pub async fn get<K, V>(&self, key: K) -> anyhow::Result<Option<V>>
where
K: ToRedisArgs + Display,
V: FromRedisValue,
{
let mut redis = self.instance().await?;
let key = format!("{}:{}", redis.db_name, key);
let value = redis
.connection
.query::<Option<V>>(cmd("GET").arg(key))
.await?;
Ok(value)
}
pub async fn hset<K, V>(&self, table: &str, key: K, value: V) -> anyhow::Result<()>
where
K: ToRedisArgs,
V: ToRedisArgs,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
redis
.connection
.exec(cmd("HSET").arg(table).arg(key).arg(value))
.await?;
Ok(())
}
pub async fn hmset<K, V>(&self, table: &str, pairs: &[(K, V)]) -> anyhow::Result<()>
where
K: ToRedisArgs,
V: ToRedisArgs,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
let mut command = cmd("HSET");
command.arg(table);
for (key, value) in pairs {
command.arg(key);
command.arg(value);
}
redis.connection.exec(&mut command).await?;
Ok(())
}
pub async fn hset_ex<K, V>(&self, table: &str, key: K, value: V, ex: u32) -> anyhow::Result<()>
where
K: ToRedisArgs,
V: ToRedisArgs,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
redis
.connection
.exec(cmd("HSET").arg(table).arg(key).arg(value).arg("EX").arg(ex))
.await?;
Ok(())
}
pub async fn hget<K, V>(&self, table: &str, key: K) -> anyhow::Result<Option<V>>
where
K: ToRedisArgs,
V: FromRedisValue,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
let value = redis
.connection
.query::<Option<V>>(cmd("HGET").arg(table).arg(key))
.await?;
Ok(value)
}
pub async fn hmget<K, V>(&self, table: &str, keys: &[K]) -> anyhow::Result<Vec<V>>
where
K: ToRedisArgs,
V: FromRedisValue,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
let items: Vec<V> = redis
.connection
.query(cmd("HMGET").arg(table).arg(keys))
.await?;
Ok(items)
}
pub async fn hget_all<K, V>(&self, table: &str) -> anyhow::Result<Vec<(K, V)>>
where
K: FromRedisValue,
V: FromRedisValue,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
let value = redis
.connection
.query::<Vec<(K, V)>>(cmd("HGETALL").arg(table))
.await?;
Ok(value)
}
pub async fn hdel<K>(&self, table: &str, key: K) -> anyhow::Result<()>
where
K: ToRedisArgs,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
redis
.connection
.exec(cmd("HDEL").arg(table).arg(key))
.await?;
Ok(())
}
pub async fn hmdel<K>(&self, table: &str, keys: &[K]) -> anyhow::Result<()>
where
K: ToRedisArgs,
{
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
let mut command = cmd("HDEL");
command.arg(table);
for key in keys {
command.arg(key);
}
redis.connection.exec(&mut command).await?;
Ok(())
}
pub async fn hdel_all(&self, table: &str) -> anyhow::Result<()> {
let mut redis = self.instance().await?;
let table = format!("{}:{}", redis.db_name, table);
redis.connection.exec(cmd("DEL").arg(table)).await?;
Ok(())
}
pub async fn publish<C, M>(&self, channel: C, message: M) -> anyhow::Result<()>
where
C: ToRedisArgs + Display,
M: ToRedisArgs + Send + Sync,
{
let mut redis = self.instance().await?;
let channel = format!("{}:{}", redis.db_name, channel);
redis
.connection
.publish(channel.as_bytes(), message)
.await?;
Ok(())
}
}