use crate::cache::{Cache, CacheRead, CacheWrite, Storage};
use crate::errors::*;
use redis::aio::Connection;
use redis::{cmd, Client, InfoDict};
use rredis as redis;
use std::collections::HashMap;
use std::io::Cursor;
use std::time::{Duration, Instant};
use url::Url;
#[derive(Clone)]
pub struct RedisCache {
display_url: String, client: Client,
}
impl RedisCache {
pub fn new(url: &str) -> Result<RedisCache> {
let mut parsed = Url::parse(url)?;
if parsed.password().is_some() {
let _ = parsed.set_password(Some("*****"));
}
Ok(RedisCache {
display_url: parsed.to_string(),
client: Client::open(url)?,
})
}
async fn connect(&self) -> Result<Connection> {
Ok(self.client.get_async_connection().await?)
}
}
#[async_trait]
impl Storage for RedisCache {
async fn get(&self, key: &str) -> Result<Cache> {
let mut c = self.connect().await?;
let d: Vec<u8> = cmd("GET").arg(key).query_async(&mut c).await?;
if d.is_empty() {
Ok(Cache::Miss)
} else {
CacheRead::from(Cursor::new(d)).map(Cache::Hit)
}
}
async fn put(&self, key: &str, entry: CacheWrite) -> Result<Duration> {
let start = Instant::now();
let mut c = self.connect().await?;
let d = entry.finish()?;
cmd("SET").arg(key).arg(d).query_async(&mut c).await?;
Ok(start.elapsed())
}
fn location(&self) -> String {
format!("Redis: {}", self.display_url)
}
async fn current_size(&self) -> Result<Option<u64>> {
let mut c = self.connect().await?;
let v: InfoDict = cmd("INFO").query_async(&mut c).await?;
Ok(v.get("used_memory"))
}
async fn max_size(&self) -> Result<Option<u64>> {
let mut c = self.connect().await?;
let result: redis::RedisResult<HashMap<String, usize>> = cmd("CONFIG")
.arg("GET")
.arg("maxmemory")
.query_async(&mut c)
.await;
match result {
Ok(h) => Ok(h
.get("maxmemory")
.and_then(|&s| if s != 0 { Some(s as u64) } else { None })),
Err(_) => Ok(None),
}
}
}