use std::time::Duration; use config::redis::RedisConfig; use redis::{AsyncCommands, Cmd, FromRedisValue, ToRedisArgs, ToSingleRedisArg}; use crate::conn::{CacheConn, CacheConnection}; use crate::error::{CacheError, CacheResult}; #[derive(Clone)] pub struct Cache { conn: CacheConn, prefix: String, default_ttl: Option, } impl Cache { pub async fn connect(cfg: &RedisConfig) -> CacheResult { let conn = CacheConn::init(cfg).await?; Ok(Self::from_conn(conn, cfg)) } pub fn from_conn(conn: CacheConn, cfg: &RedisConfig) -> Self { Self { conn, prefix: cfg.key_prefix().to_string(), default_ttl: cfg.default_ttl(), } } pub fn conn(&self) -> &CacheConn { &self.conn } pub fn key_prefix(&self) -> &str { &self.prefix } pub fn default_ttl(&self) -> Option { self.default_ttl } pub fn is_cluster(&self) -> bool { self.conn.is_cluster() } pub fn database(&self) -> i64 { self.conn.database() } pub async fn acquire(&self) -> CacheResult { self.conn.acquire().await } pub async fn ping(&self) -> CacheResult<()> { let mut cmd = Cmd::new(); cmd.arg("PING"); self.exec_unit(&cmd).await } pub fn pipeline(&self) -> crate::pipeline::CachePipeline { crate::pipeline::CachePipeline::new(self.clone()) } pub fn scripts(&self) -> crate::scripts::LuaScripts { crate::scripts::LuaScripts::new(self.clone()) } pub(crate) async fn exec(&self, cmd: &Cmd) -> CacheResult { let mut conn = self.conn.clone(); cmd.query_async::(&mut conn) .await .map_err(CacheError::from) } pub(crate) async fn exec_unit(&self, cmd: &Cmd) -> CacheResult<()> { self.exec::<()>(cmd).await } pub async fn get(&self, key: &str) -> CacheResult> { let mut conn = self.conn.clone(); conn.get(key).await.map_err(CacheError::from) } pub async fn get_bytes(&self, key: &str) -> CacheResult>> { self.get::>(key).await } pub async fn set(&self, key: &str, value: V) -> CacheResult<()> { self.set_with(key, value, None, false, false).await } pub async fn set_ex( &self, key: &str, value: V, ttl: Duration, ) -> CacheResult<()> { self.set_with(key, value, Some(ttl), false, false).await } pub async fn set_default( &self, key: &str, value: V, ) -> CacheResult<()> { self.set_with(key, value, self.default_ttl, false, false) .await } pub async fn set_nx_ex( &self, key: &str, value: V, ttl: Duration, ) -> CacheResult { self.set_flag(key, value, Some(ttl), true, false).await } async fn set_with( &self, key: &str, value: V, ttl: Option, nx: bool, xx: bool, ) -> CacheResult<()> { if nx || xx { self.set_flag(key, value, ttl, nx, xx).await?; return Ok(()); } let cmd = set_cmd(key, value, ttl, false, false); self.exec_unit(&cmd).await } async fn set_flag( &self, key: &str, value: V, ttl: Option, nx: bool, xx: bool, ) -> CacheResult { let cmd = set_cmd(key, value, ttl, nx, xx); self.exec(&cmd).await } pub async fn append( &self, key: &str, value: V, ) -> CacheResult { let mut conn = self.conn.clone(); conn.append(key, value).await.map_err(CacheError::from) } pub async fn strlen(&self, key: &str) -> CacheResult { let mut conn = self.conn.clone(); conn.strlen(key).await.map_err(CacheError::from) } pub async fn incr(&self, key: &str) -> CacheResult { self.incr_by(key, 1).await } pub async fn incr_by(&self, key: &str, delta: i64) -> CacheResult { let mut conn = self.conn.clone(); conn.incr(key, delta).await.map_err(CacheError::from) } pub async fn decr(&self, key: &str) -> CacheResult { self.decr_by(key, 1).await } pub async fn decr_by(&self, key: &str, delta: i64) -> CacheResult { let mut conn = self.conn.clone(); conn.decr(key, delta).await.map_err(CacheError::from) } pub async fn exists(&self, key: &str) -> CacheResult { let mut conn = self.conn.clone(); conn.exists(key).await.map_err(CacheError::from) } pub async fn expire(&self, key: &str, ttl: Duration) -> CacheResult { let mut conn = self.conn.clone(); conn.pexpire(key, millis(ttl)) .await .map_err(CacheError::from) } pub async fn persist(&self, key: &str) -> CacheResult { let mut conn = self.conn.clone(); conn.persist(key).await.map_err(CacheError::from) } pub async fn ttl(&self, key: &str) -> CacheResult> { let mut conn = self.conn.clone(); let seconds: i64 = conn.ttl(key).await.map_err(CacheError::from)?; Ok({ if seconds < 0 { None } else { Some(Duration::from_secs(seconds as u64)) } }) } pub async fn delete(&self, key: &str) -> CacheResult { let mut conn = self.conn.clone(); let removed: usize = conn.del(key).await.map_err(CacheError::from)?; Ok(removed > 0) } pub async fn delete_many(&self, keys: K) -> CacheResult { let mut conn = self.conn.clone(); conn.del(keys).await.map_err(CacheError::from) } pub async fn unlink(&self, keys: K) -> CacheResult { let mut conn = self.conn.clone(); conn.unlink(keys).await.map_err(CacheError::from) } pub async fn mget( &self, keys: K, ) -> CacheResult>> { let mut conn = self.conn.clone(); conn.mget(keys).await.map_err(CacheError::from) } pub async fn mset( &self, items: &[(K, V)], ) -> CacheResult<()> { let mut conn = self.conn.clone(); conn.mset(items).await.map_err(CacheError::from) } pub async fn rename(&self, key: &str, new_key: &str) -> CacheResult<()> { let mut conn = self.conn.clone(); conn.rename(key, new_key).await.map_err(CacheError::from) } } fn set_cmd( key: &str, value: V, ttl: Option, nx: bool, xx: bool, ) -> Cmd { let mut cmd = Cmd::new(); cmd.arg("SET").arg(key).arg(value); if let Some(ttl) = ttl { cmd.arg("PX").arg(millis(ttl)); } if nx { cmd.arg("NX"); } if xx { cmd.arg("XX"); } cmd } pub(crate) fn millis(ttl: Duration) -> i64 { let millis = ttl.as_millis(); if millis == 0 { 1 } else if millis > i64::MAX as u128 { i64::MAX } else { millis as i64 } }