//! Caching support to prevent hitting the API a lot. use std::{error::Error, fmt, fs, path::Path}; use xdg::BaseDirectories; use super::APP_NAME; #[derive(Debug)] pub enum CacheError { Io(std::io::Error), } error_froms! { CacheError, err: std::io::Error => CacheError::Io(err), } impl fmt::Display for CacheError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { CacheError::Io(_) => write!(f, "cache input/output error"), } } } impl Error for CacheError { fn source(&self) -> Option<&(dyn Error + 'static)> { match *self { CacheError::Io(ref err) => Some(err), } } } /// A generic cache. pub trait Cache { fn store(&mut self, path: &Path, data: &[u8]) -> Result<(), CacheError>; fn get(&mut self, path: &Path) -> Result>, CacheError>; } /// A cache that stores files in the XDG specified cache location. pub struct FileCache { dirs: BaseDirectories, } impl FileCache { /// Create a new file backed cache. pub fn new() -> Self { let dirs = BaseDirectories::with_prefix(APP_NAME).unwrap(); FileCache { dirs } } } impl Cache for FileCache { fn store(&mut self, path: &Path, data: &[u8]) -> Result<(), CacheError> { let cache_path = self.dirs.place_cache_file(path).unwrap(); fs::write(cache_path, data)?; Ok(()) } fn get(&mut self, path: &Path) -> Result>, CacheError> { let cache_path = self.dirs.find_cache_file(path); match cache_path { Some(path) => Ok(Some(fs::read(path)?)), None => Ok(None), } } } /// A cache that does nothing. pub struct NoopCache; impl NoopCache { pub fn new() -> Self { NoopCache } } impl Cache for NoopCache { fn store(&mut self, _path: &Path, _data: &[u8]) -> Result<(), CacheError> { Ok(()) } fn get(&mut self, _path: &Path) -> Result>, CacheError> { Ok(None) } }