use std::error::Error;
use std::io::Read;
use std::sync::{Arc, RwLock};
use std::hash::Hash;
use std::{fs, thread};
use std::time::Duration;
use tokio::sync::RwLock as TokioRwLock;
pub struct Cache<K,V> where
K: Hash + std::cmp::Eq + Clone,
{
map: Arc<RwLock<std::collections::HashMap<K, Arc<V>>>>,
generator: Generator<K, V>,
}
pub struct CacheAsync<K, V> where
K: Hash + std::cmp::Eq + Clone,
{
map: Arc<TokioRwLock<std::collections::HashMap<K, Arc<V>>>>,
generator: GeneratorAsync<K, V>,
}
struct Generator<K, V> where
K:Hash + Eq + Clone
{
generator: Box<dyn Fn(&K) -> V + 'static + Sync>
}
struct GeneratorAsync<K, V> where
K:Hash + Eq + Clone
{
generator: Box<dyn Fn(&K) -> V + 'static + Sync>
}
impl <K, V> Cache<K,V> where
K:Hash + Eq + Clone
{
pub fn new(generator:impl Fn(&K) -> V + 'static + Sync) -> Self
where K: Hash + Eq {
Cache {
map: Arc::new(RwLock::new(std::collections::HashMap::new())),
generator: Generator{generator: Box::new(generator)},
}
}
pub fn get_if(&self, key: &K) -> Option<Arc<V>> {
let r = self.map.read().unwrap();
let value = r.get(key);
match value {
Some(v) => Some(Arc::clone(v)),
None => None
}
}
pub fn drop(&self, key: &K) {
let mut w = self.map.write().unwrap();
w.remove(key);
}
pub fn get(&self, key: &K) -> Arc<V> {
let r = self.map.read().unwrap();
let value = r.get(key);
match value {
Some(v) => Arc::clone(v),
None => {
drop(r);
let mut w = self.map.write().unwrap();
let value = (self.generator.generator)(key);
let arc = Arc::new(value);
w.insert(key.clone(), Arc::clone(&arc));
drop(w);
arc
}
}
}
}
impl <K, V> CacheAsync<K,V> where
K:Hash + Eq + Clone
{
pub fn new(generator:impl Fn(&K) -> V + 'static + Sync) -> Self
where K: Hash + Eq {
CacheAsync {
map: Arc::new(TokioRwLock::new(std::collections::HashMap::new())),
generator: GeneratorAsync{generator: Box::new(generator)},
}
}
pub async fn get_if(&self, key: &K) -> Option<Arc<V>> {
let r = self.map.read().await;
let value = r.get(key);
match value {
Some(v) => Some(Arc::clone(v)),
None => None
}
}
pub async fn drop(&self, key: &K) {
let mut w = self.map.write().await;
w.remove(key);
}
pub async fn get(&self, key: &K) -> Arc<V> {
let r = self.map.read().await;
let value = r.get(key);
match value {
Some(v) => Arc::clone(v),
None => {
drop(r);
let mut w = self.map.write().await;
let value = (self.generator.generator)(key);
let arc = Arc::new(value);
w.insert(key.clone(), Arc::clone(&arc));
drop(w);
arc
}
}
}
}
pub struct FromWatchedFile<T> {
value: Arc<RwLock<Result<Arc<T>, FileParseError>>>,
}
#[derive(Debug, Clone)]
pub struct FileParseError {
cause: String
}
impl From<String> for FileParseError {
fn from(value: String) -> Self {
FileParseError {
cause: value
}
}
}
impl From<&str> for FileParseError {
fn from(value:&str) -> Self {
value.to_string().into()
}
}
impl From<Box<dyn Error>> for FileParseError {
fn from(value: Box<dyn Error>) -> Self {
format!("{}", value).into()
}
}
impl std::fmt::Display for FileParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.cause)
}
}
impl Error for FileParseError {
}
impl<T> FromWatchedFile<T>
where
T: Send + Sync + 'static,
{
fn read_file(file_path: &str) -> Result<Vec<u8>, std::io::Error> {
let mut file = fs::File::open(file_path)?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
Ok(contents)
}
pub fn new<F>(file_path: &str, parser: F, interval: Duration) -> Self
where
F: Fn(&[u8]) -> Result<T, FileParseError> + Send + Sync + 'static
{
let current_content = Self::read_file(file_path);
let value = match current_content {
Ok(content) => {
let parsed = parser(&content);
match parsed {
Ok(v) => {
Arc::new(RwLock::new(Ok(Arc::new(v))))
},
Err(cause) => {
Arc::new(RwLock::new(Err(cause)))
}
}
},
Err(cause) => {
let err:Box<dyn Error> = Box::new(cause);
Arc::new(RwLock::new(Err(FileParseError::from(err))))
}
};
let value_clone = value.clone();
let file_path = file_path.to_string();
let mut last_modified = fs::metadata(&file_path).ok().and_then(|m| m.modified().ok());
thread::spawn(move || {
loop {
thread::sleep(interval);
let metadata = fs::metadata(&file_path).ok();
let modified = metadata.and_then(|m| m.modified().ok());
if modified != last_modified {
let content = Self::read_file(file_path.as_str());
last_modified = modified;
match content {
Ok(bytes) => {
let parsed_value = parser(&bytes);
match parsed_value {
Ok(v) => {
let mut w = value_clone.write().unwrap();
*w =Ok(Arc::new(v));
},
Err(_) => {
}
}
},
Err(_) => {
}
}
}
}
});
return Self{
value
}
}
pub fn get<'a>(&'a self) -> Result<Arc<T>, FileParseError>
{
let result = self.value.read().unwrap();
match result.as_ref() {
Ok(what) => {
Ok(Arc::clone(what))
},
Err(cause) => {
Err(cause.clone())
}
}
}
}
#[cfg(test)]
mod tests {
use thread::sleep;
use super::*;
#[test]
fn it_works() {
println!("Running test...");
let c = Cache::new(|x:&String| -> String {
println!("Generating {}", x);
let mut y = x.clone();
y.push_str("@");
y
});
for j in 0..2 {
for i in 0..10 {
let key = format!("key{}", i);
let v = c.get(&key);
println!("{}:{}: {}", j, i, *v);
}
}
}
#[tokio::test]
async fn test_cache_async() {
println!("Running test...");
let c = CacheAsync::new(|x:&String| -> String {
println!("Generating {}", x);
let mut y = x.clone();
y.push_str("@");
y
});
for j in 0..2 {
for i in 0..10 {
let key = format!("key{}", i);
let v = c.get(&key).await;
println!("{}:{}: {}", j, i, *v);
}
}
}
#[test]
fn test_load_file() {
fn file_to_string(bytes: &[u8]) -> Result<String, FileParseError> {
Ok(String::from_utf8_lossy(bytes).to_string())
}
let cfg: FromWatchedFile<String> = FromWatchedFile::new("config.json", file_to_string, Duration::from_secs(5));
for _i in 0..100 {
let config = cfg.get();
match config {
Ok(c) => println!("Config: {}", c),
Err(_)=> println!("Config not loaded yet"),
}
sleep(Duration::from_secs(1));
}
}
}