You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.5 KiB
103 lines
3.5 KiB
//! 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<T>
|
|
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<T>;
|
|
/// 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<Self> {
|
|
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<T>(&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<T>(&self, key: &str) -> Result<T>
|
|
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)
|
|
}
|
|
}
|