use std::cell::RefCell;
use std::rc::Rc;
use redis::cluster::ClusterClient;
use redis::Client;
use redis::FromRedisValue;
use redis::{aio::ConnectionManager, cluster::ClusterConnection};
pub mod mcaptcha_redis;
use crate::errors::*;
#[derive(Clone)]
pub enum RedisConfig {
Single(String),
Cluster(Vec<String>),
}
impl RedisConfig {
pub async fn connect(&self) -> RedisClient {
match self {
Self::Single(url) => {
let client = ConnectionManager::new(Client::open(url.as_str()).unwrap())
.await
.unwrap();
RedisClient::Single(client)
}
Self::Cluster(nodes) => {
let cluster_client = ClusterClient::open(nodes.to_owned()).unwrap();
RedisClient::Cluster(cluster_client)
}
}
}
}
#[derive(Clone)]
pub enum RedisConnection {
Single(Rc<RefCell<ConnectionManager>>),
Cluster(Rc<RefCell<ClusterConnection>>),
}
impl RedisConnection {
#[inline]
pub fn get_client(&self) -> Self {
match self {
Self::Single(con) => Self::Single(Rc::clone(con)),
Self::Cluster(con) => Self::Cluster(Rc::clone(con)),
}
}
#[inline]
pub async fn exec<T: FromRedisValue>(&self, cmd: &mut redis::Cmd) -> redis::RedisResult<T> {
match self {
RedisConnection::Single(con) => cmd.query_async(&mut *con.borrow_mut()).await,
RedisConnection::Cluster(con) => cmd.query(&mut *con.borrow_mut()),
}
}
pub async fn ping(&self) -> bool {
if let Ok(redis::Value::Status(v)) = self.exec(&mut redis::cmd("PING")).await {
v == "PONG"
} else {
false
}
}
}
#[derive(Clone)]
pub enum RedisClient {
Single(ConnectionManager),
Cluster(ClusterClient),
}
#[derive(Clone)]
pub struct Redis {
_client: RedisClient,
connection: RedisConnection,
}
impl Redis {
pub async fn new(redis: RedisConfig) -> CaptchaResult<Self> {
let (_client, connection) = Self::connect(redis).await?;
let master = Self {
_client,
connection,
};
Ok(master)
}
pub fn get_client(&self) -> RedisConnection {
self.connection.get_client()
}
async fn connect(redis: RedisConfig) -> CaptchaResult<(RedisClient, RedisConnection)> {
let redis = redis.connect().await;
let client = match &redis {
RedisClient::Single(c) => RedisConnection::Single(Rc::new(RefCell::new(c.clone()))),
RedisClient::Cluster(c) => {
let con = c.get_connection()?;
RedisConnection::Cluster(Rc::new(RefCell::new(con)))
}
};
Ok((redis, client))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[actix_rt::test]
async fn ping_works() {
let r = Redis::new(RedisConfig::Single("redis://127.0.0.1".into()))
.await
.unwrap();
assert!(r.get_client().ping().await);
}
}