use cache::{
Cache,
CacheRead,
CacheWrite,
Storage,
};
use futures_cpupool::CpuPool;
use lru_disk_cache::LruDiskCache;
use lru_disk_cache::Error as LruError;
use std::ffi::OsStr;
use std::path::{Path,PathBuf};
use std::sync::{Arc, Mutex};
use std::time::{Instant, Duration};
use errors::*;
#[derive(Clone)]
pub struct DiskCache {
lru: Arc<Mutex<LruDiskCache>>,
pool: CpuPool,
}
impl DiskCache {
pub fn new<T: AsRef<OsStr>>(root: &T,
max_size: u64,
pool: &CpuPool) -> DiskCache {
DiskCache {
lru: Arc::new(Mutex::new(LruDiskCache::new(root, max_size).expect("Couldn't instantiate disk cache!"))),
pool: pool.clone(),
}
}
}
fn make_key_path(key: &str) -> PathBuf {
Path::new(&key[0..1]).join(&key[1..2]).join(key)
}
impl Storage for DiskCache {
fn get(&self, key: &str) -> SFuture<Cache> {
trace!("DiskCache::get({})", key);
let path = make_key_path(key);
let lru = self.lru.clone();
let key = key.to_owned();
Box::new(self.pool.spawn_fn(move || {
let mut lru = lru.lock().unwrap();
let f = match lru.get(&path) {
Ok(f) => f,
Err(LruError::FileNotInCache) => {
trace!("DiskCache::get({}): FileNotInCache", key);
return Ok(Cache::Miss);
}
Err(LruError::Io(e)) => {
trace!("DiskCache::get({}): IoError: {:?}", key, e);
return Err(e.into());
}
Err(_) => panic!("Unexpected error!"),
};
let hit = CacheRead::from(f)?;
Ok(Cache::Hit(hit))
}))
}
fn put(&self, key: &str, entry: CacheWrite) -> SFuture<Duration> {
trace!("DiskCache::finish_put({})", key);
let lru = self.lru.clone();
let key = make_key_path(key);
Box::new(self.pool.spawn_fn(move || {
let start = Instant::now();
let v = entry.finish()?;
lru.lock().unwrap().insert_bytes(key, &v)?;
Ok(start.elapsed())
}))
}
fn location(&self) -> String {
format!("Local disk: {:?}", self.lru.lock().unwrap().path())
}
fn current_size(&self) -> Option<u64> { Some(self.lru.lock().unwrap().size()) }
fn max_size(&self) -> Option<u64> { Some(self.lru.lock().unwrap().capacity()) }
}