Import the memory cache
This commit is contained in:
parent
b15658dd0c
commit
5431f1e37f
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "cachememory"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = { version = "0.4" }
|
||||||
|
futures = { version = "0.3" }
|
||||||
|
serde_derive = { version = "1" }
|
||||||
|
serde = { version = "1" }
|
||||||
|
|
||||||
|
#[dev-dependencies]
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use futures::Future;
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CacheRecord<T> {
|
||||||
|
pub timeout: DateTime<Utc>,
|
||||||
|
pub value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Cache<T>(Arc<RwLock<HashMap<String, CacheRecord<T>>>>);
|
||||||
|
|
||||||
|
impl<T: Clone> Cache<T> {
|
||||||
|
pub fn new() -> Cache<T> {
|
||||||
|
Cache(Arc::new(RwLock::new(HashMap::new())))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn find(&self, key: &str, f: impl Future<Output = (DateTime<Utc>, T)>) -> T {
|
||||||
|
let val = {
|
||||||
|
let cache = self.0.read().unwrap();
|
||||||
|
cache.get(key).cloned()
|
||||||
|
};
|
||||||
|
match val {
|
||||||
|
Some(ref val) if val.timeout > Utc::now() => val.value.clone(),
|
||||||
|
_ => {
|
||||||
|
let response = f.await;
|
||||||
|
let mut cache = self.0.write().unwrap();
|
||||||
|
let record = CacheRecord {
|
||||||
|
timeout: response.0,
|
||||||
|
value: response.1.clone(),
|
||||||
|
};
|
||||||
|
cache
|
||||||
|
.entry(key.to_owned())
|
||||||
|
.and_modify(|rec| *rec = record.clone())
|
||||||
|
.or_insert(record);
|
||||||
|
response.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use chrono::Duration;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
struct Value(i64);
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_calls_function_when_element_does_not_exist() {
|
||||||
|
let cache = Cache::new();
|
||||||
|
let value = cache
|
||||||
|
.find("my_key", async { (Utc::now(), Value(15)) })
|
||||||
|
.await;
|
||||||
|
assert_eq!(value, Value(15));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_calls_function_when_element_is_old() {
|
||||||
|
let calls = Arc::new(RwLock::new(false));
|
||||||
|
let cache = Cache::new();
|
||||||
|
let _ = cache
|
||||||
|
.find("my_key", async {
|
||||||
|
(Utc::now() - Duration::seconds(10), Value(15))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
let value = cache
|
||||||
|
.find("my_key", async {
|
||||||
|
*calls.write().unwrap() = true;
|
||||||
|
(Utc::now(), Value(16))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
assert_eq!(value, Value(16));
|
||||||
|
assert_eq!(*calls.read().unwrap(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn it_does_not_call_function_when_element_is_not_old() {
|
||||||
|
let calls = Arc::new(RwLock::new(false));
|
||||||
|
let cache = Cache::new();
|
||||||
|
let _ = cache
|
||||||
|
.find("my_key", async {
|
||||||
|
(Utc::now() + Duration::seconds(10), Value(15))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
let value = cache
|
||||||
|
.find("my_key", async {
|
||||||
|
*calls.write().unwrap() = true;
|
||||||
|
(Utc::now(), Value(16))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
assert_eq!(value, Value(15));
|
||||||
|
assert_eq!(*calls.read().unwrap(), false);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue