//! Module to implement a simple cache use crate::errors::*; use core::time::Duration; use log::{debug, trace}; use serde::Serialize; use std::fs::File as FsFile; use std::io::{BufReader, BufWriter}; use std::path::PathBuf; use std::time::SystemTime; /// Trait for a simple cache /// /// Each struct implementing this trais has to offer functions to `get`, `set` an object and check /// if an value `exists`. pub trait Cache where T: Serialize + serde::de::DeserializeOwned { /// Stores the `data` under the given `key` fn set(&self, key: &str, data: &T) -> Result<()>; /// Retrieves the data under the given `key` fn get(&self, key: &str) -> Result; /// Checks whether the given key exists fn exists(&self, key: &str) -> bool; } /// A File cache #[derive(Debug)] pub struct File { /// The root path to the files root: PathBuf, /// Time to live for items ttl: Duration, } impl File { /// Create a new File cache /// /// The cached objects will be stored in the provided root folder. /// The folder will be created, if it does not yet exist. /// /// ## Errors /// The function returns an `Err` if the creation of the folders fails pub fn new(root: &PathBuf, ttl: Duration) -> Result { if !root.exists() { std::fs::create_dir_all(root) .chain_err(|| "Error while creating dirs for file cache")?; } Ok(Self { root: root.clone(), ttl }) } /// Checks whether an element for the given key exists pub fn exists(&self, key: &str) -> bool { trace!("Check existence of {} in file cache", key); let mut path = self.root.clone(); path.push(key); if !path.exists() { return false; } let modified: SystemTime = path .metadata() .map(|meta| meta.modified().unwrap_or(SystemTime::UNIX_EPOCH)) .unwrap_or(SystemTime::UNIX_EPOCH); let age = modified.elapsed().unwrap_or(Duration::from_secs(u64::max_value())); debug!("Age of cache file: {} s", age.as_secs()); if age > self.ttl { debug!("Cache file is too old (> {} seconds), won't be used", self.ttl.as_secs()); return false; } true } /// Stores a `data` under the specified `key` pub fn set(&self, key: &str, data: &T) -> Result<()> where T: Serialize { let mut path: PathBuf = self.root.clone(); path.push(key); trace!("Serializing data to cache file {}", path.to_string_lossy()); let cache = FsFile::create(path).chain_err(|| "Error while creating file cache to write")?; let writer = BufWriter::new(cache); serde_json::to_writer(writer, &data) .chain_err(|| "Error while serialzing templates to file cache")?; debug!("Serialization of data completed"); Ok(()) } /// Retrieves data for `key` pub fn get(&self, key: &str) -> Result where T: serde::de::DeserializeOwned { let mut path = self.root.clone(); path.push(key); debug!("Retrieving {} from file cache", key); let f = FsFile::open(path) .chain_err(|| format!("Error while opening cache file for key {}", key))?; let reader = BufReader::new(f); let obj: T = serde_json::from_reader(reader) .chain_err(|| "Error while reading templates from file cache")?; debug!("Deserialized {} templates from file cache", "some"); Ok(obj) } }