use std::borrow::Borrow;
use std::cmp::Ord;
use std::collections::btree_map;
use std::collections::hash_map;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::{BuildHasher, Hash};
use std::time::{Duration, Instant};
pub trait CacheMapExt<K, V> {
fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q> + Ord,
Q: Hash + Eq + Ord + ?Sized;
fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q> + Ord,
Q: Hash + Eq + Ord + ?Sized;
fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V>;
fn remove_expired_values(&mut self);
}
pub trait EntryExt<'a, K, V> {
fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V;
fn or_insert_with_timeout_f<F: FnOnce() -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V;
fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V;
fn and_modify_with_timeout<F>(self, f: F) -> Self
where
F: FnOnce(&mut V);
}
impl<K, V, S> CacheMapExt<K, V> for HashMap<K, TimedValue<V>, S>
where
K: Eq + Hash,
S: BuildHasher,
{
fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.get(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value())
}
})
}
fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.get_mut(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value_mut())
}
})
}
fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
self.insert(k, TimedValue::new(v, timeout))
.map(|tv| tv.into_value())
}
fn remove_expired_values(&mut self) {
self.retain(|_, tv| !tv.is_expired());
}
}
impl<'a, K, V> EntryExt<'a, K, V> for hash_map::Entry<'a, K, TimedValue<V>>
where
K: Eq + Hash,
{
fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
match self {
hash_map::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default, timeout);
}
v.value_mut()
}
hash_map::Entry::Vacant(entry) => {
entry.insert(TimedValue::new(default, timeout)).value_mut()
}
}
}
fn or_insert_with_timeout_f<F: FnOnce() -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
hash_map::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default(), timeout);
}
v.value_mut()
}
hash_map::Entry::Vacant(entry) => entry
.insert(TimedValue::new(default(), timeout))
.value_mut(),
}
}
fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
hash_map::Entry::Occupied(entry) => {
let value = if entry.get().is_expired() {
Some(default(entry.key()))
} else {
None
};
let v = entry.into_mut();
if let Some(value) = value {
*v = TimedValue::new(value, timeout);
}
v.value_mut()
}
hash_map::Entry::Vacant(entry) => {
let value = default(entry.key());
entry.insert(TimedValue::new(value, timeout)).value_mut()
}
}
}
fn and_modify_with_timeout<F>(self, f: F) -> Self
where
F: FnOnce(&mut V),
{
self.and_modify(|v| f(v.value_mut()))
}
}
impl<K: Ord, V> CacheMapExt<K, V> for BTreeMap<K, TimedValue<V>> {
fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
self.get(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value())
}
})
}
fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
self.get_mut(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value_mut())
}
})
}
fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
self.insert(k, TimedValue::new(v, timeout))
.map(|tv| tv.into_value())
}
fn remove_expired_values(&mut self) {
self.retain(|_, tv| !tv.is_expired());
}
}
impl<'a, K, V> EntryExt<'a, K, V> for btree_map::Entry<'a, K, TimedValue<V>>
where
K: Ord,
{
fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
match self {
btree_map::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default, timeout);
}
v.value_mut()
}
btree_map::Entry::Vacant(entry) => {
entry.insert(TimedValue::new(default, timeout)).value_mut()
}
}
}
fn or_insert_with_timeout_f<F: FnOnce() -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
btree_map::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default(), timeout);
}
v.value_mut()
}
btree_map::Entry::Vacant(entry) => entry
.insert(TimedValue::new(default(), timeout))
.value_mut(),
}
}
fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
btree_map::Entry::Occupied(entry) => {
let value = if entry.get().is_expired() {
Some(default(entry.key()))
} else {
None
};
let v = entry.into_mut();
if let Some(value) = value {
*v = TimedValue::new(value, timeout);
}
v.value_mut()
}
btree_map::Entry::Vacant(entry) => {
let value = default(entry.key());
entry.insert(TimedValue::new(value, timeout)).value_mut()
}
}
}
fn and_modify_with_timeout<F>(self, f: F) -> Self
where
F: FnOnce(&mut V),
{
self.and_modify(|v| f(v.value_mut()))
}
}
impl<K: Ord + Clone, V> CacheMapExt<K, V> for dequemap::DequeBTreeMap<K, TimedValue<V>> {
fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
self.get(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value())
}
})
}
fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Ord + ?Sized,
{
self.get_mut(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value_mut())
}
})
}
fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
self.insert(k, TimedValue::new(v, timeout))
.map(|tv| tv.into_value())
}
fn remove_expired_values(&mut self) {
self.retain(|_, tv| !tv.is_expired());
}
}
impl<'a, K, V> EntryExt<'a, K, V> for dequemap::btreemap::Entry<'a, K, TimedValue<V>>
where
K: Ord + Clone,
{
fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
match self {
dequemap::btreemap::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default, timeout);
}
v.value_mut()
}
dequemap::btreemap::Entry::Vacant(entry) => {
entry.insert(TimedValue::new(default, timeout)).value_mut()
}
}
}
fn or_insert_with_timeout_f<F: FnOnce() -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
dequemap::btreemap::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default(), timeout);
}
v.value_mut()
}
dequemap::btreemap::Entry::Vacant(entry) => entry
.insert(TimedValue::new(default(), timeout))
.value_mut(),
}
}
fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
dequemap::btreemap::Entry::Occupied(entry) => {
let value = if entry.get().is_expired() {
Some(default(entry.key()))
} else {
None
};
let v = entry.into_mut();
if let Some(value) = value {
*v = TimedValue::new(value, timeout);
}
v.value_mut()
}
dequemap::btreemap::Entry::Vacant(entry) => {
let value = default(entry.key());
entry.insert(TimedValue::new(value, timeout)).value_mut()
}
}
}
fn and_modify_with_timeout<F>(self, f: F) -> Self
where
F: FnOnce(&mut V),
{
self.and_modify(|v| f(v.value_mut()))
}
}
impl<K, V, S> CacheMapExt<K, V> for dequemap::DequeHashMap<K, TimedValue<V>, S>
where
K: Hash + Eq + Ord + Clone,
S: BuildHasher,
{
fn get_with_timeout<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.get(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value())
}
})
}
fn get_with_timeout_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.get_mut(k).and_then(|tv| {
if tv.is_expired() {
None
} else {
Some(tv.value_mut())
}
})
}
fn insert_with_timeout(&mut self, k: K, v: V, timeout: Option<Duration>) -> Option<V> {
self.insert(k, TimedValue::new(v, timeout))
.map(|tv| tv.into_value())
}
fn remove_expired_values(&mut self) {
self.retain(|_, tv| !tv.is_expired());
}
}
impl<'a, K, V, S> EntryExt<'a, K, V> for dequemap::hashmap::Entry<'a, K, TimedValue<V>, S>
where
K: Eq + Hash + Clone,
S: BuildHasher,
{
fn or_insert_with_timeout(self, default: V, timeout: Option<Duration>) -> &'a mut V {
match self {
dequemap::hashmap::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default, timeout);
}
v.value_mut()
}
dequemap::hashmap::Entry::Vacant(entry) => {
entry.insert(TimedValue::new(default, timeout)).value_mut()
}
}
}
fn or_insert_with_timeout_f<F: FnOnce() -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
dequemap::hashmap::Entry::Occupied(entry) => {
let v = entry.into_mut();
if v.is_expired() {
*v = TimedValue::new(default(), timeout);
}
v.value_mut()
}
dequemap::hashmap::Entry::Vacant(entry) => entry
.insert(TimedValue::new(default(), timeout))
.value_mut(),
}
}
fn or_insert_with_timeout_key_f<F: FnOnce(&K) -> V>(
self,
default: F,
timeout: Option<Duration>,
) -> &'a mut V {
match self {
dequemap::hashmap::Entry::Occupied(entry) => {
let value = if entry.get().is_expired() {
Some(default(entry.key()))
} else {
None
};
let v = entry.into_mut();
if let Some(value) = value {
*v = TimedValue::new(value, timeout);
}
v.value_mut()
}
dequemap::hashmap::Entry::Vacant(entry) => {
let value = default(entry.key());
entry.insert(TimedValue::new(value, timeout)).value_mut()
}
}
}
fn and_modify_with_timeout<F>(self, f: F) -> Self
where
F: FnOnce(&mut V),
{
self.and_modify(|v| f(v.value_mut()))
}
}
#[derive(Clone, Debug)]
pub struct TimedValue<V>(V, Option<Instant>);
impl<V> TimedValue<V> {
pub fn new(value: V, timeout_duration: Option<Duration>) -> Self {
TimedValue(value, timeout_duration.map(|t| Instant::now() + t))
}
pub fn value(&self) -> &V {
&self.0
}
pub fn value_mut(&mut self) -> &mut V {
&mut self.0
}
pub fn into_value(self) -> V {
self.0
}
pub fn is_expired(&self) -> bool {
self.1.map(|e| Instant::now() >= e).unwrap_or(false)
}
}
impl<V> PartialEq for TimedValue<V>
where
V: PartialEq,
{
fn eq(&self, other: &TimedValue<V>) -> bool {
self.value() == other.value()
}
}
#[test]
fn test_cache_map_ext() {
use std::collections::hash_map::RandomState;
let mut m: HashMap<_, _, RandomState> = HashMap::default();
let old1 = m.insert("k1", TimedValue::new(1, None));
let old2 = m.insert("k2", TimedValue::new(2, Some(Duration::from_millis(50))));
let old3 = m.insert("k3", TimedValue::new(3, Some(Duration::from_millis(80))));
let old4 = m.insert("k4", TimedValue::new(4, Some(Duration::from_millis(130))));
let old44 = m.insert("k4", TimedValue::new(44, None));
let old6 = m.insert_with_timeout("k6", 6, Some(Duration::from_secs(150)));
let old7 = m.insert_with_timeout("k7", 7, None);
let old66 = m.insert_with_timeout("k6", 66, Some(Duration::from_secs(60)));
assert_eq!(old1, None);
assert_eq!(old2, None);
assert_eq!(old3, None);
assert_eq!(old4, None);
assert_eq!(old6, None);
assert_eq!(old7, None);
println!("old44: {:?}", old44);
assert_eq!(old44, Some(TimedValue::new(4, None)));
assert_eq!(old66, Some(6));
println!("old66: {:?}", old66);
let v6 = m.get("k6");
println!("v6: {:?}", v6);
assert_eq!(v6, Some(&TimedValue::new(66, None)));
m.get_with_timeout_mut("k6").map(|v| *v = 666);
let v6 = m.get_with_timeout("k6");
println!("v6: {:?}", v6);
assert_eq!(v6, Some(&666));
for i in 0..20 {
m.remove_expired_values();
println!(
"{} map len: {}, map k1: {:?}, k2: {:?}",
i,
m.len(),
m.get("k1"),
m.get_with_timeout("k2")
);
std::thread::sleep(std::time::Duration::from_millis(30));
}
}
#[test]
fn test_btree_map_ext() {
let mut m: BTreeMap<_, _> = BTreeMap::default();
let old1 = m.insert_with_timeout("k1", 1, None);
let old2 = m.insert_with_timeout("k2", 2, Some(Duration::from_millis(50)));
let old3 = m.insert_with_timeout("k3", 3, Some(Duration::from_millis(80)));
let old4 = m.insert_with_timeout("k4", 4, Some(Duration::from_millis(130)));
let old44 = m.insert_with_timeout("k4", 44, None);
let old6 = m.insert_with_timeout("k6", 6, Some(Duration::from_secs(150)));
let old7 = m.insert_with_timeout("k7", 7, None);
let old66 = m.insert_with_timeout("k6", 66, Some(Duration::from_secs(60)));
assert_eq!(old1, None);
assert_eq!(old2, None);
assert_eq!(old3, None);
assert_eq!(old4, None);
assert_eq!(old6, None);
assert_eq!(old7, None);
println!("old44: {:?}", old44);
assert_eq!(old44, Some(4));
assert_eq!(m.get_with_timeout("k4"), Some(&44));
assert_eq!(old66, Some(6));
println!("old66: {:?}", old66);
let v6 = m.get("k6");
println!("v6: {:?}", v6);
assert_eq!(v6, Some(&TimedValue::new(66, None)));
m.get_with_timeout_mut("k6").map(|v| *v = 666);
let v6 = m.get_with_timeout("k6");
println!("v6: {:?}", v6);
assert_eq!(v6, Some(&666));
m.entry("kk1").or_insert_with_timeout_f(|| 10, None);
assert_eq!(m.get_with_timeout("kk1"), Some(&10));
m.entry("kk1").and_modify_with_timeout(|v| {
*v = 100;
});
assert_eq!(m.get_with_timeout("kk1"), Some(&100));
println!("kk1: {:?}", m.get_with_timeout("kk1"));
}
#[test]
fn test_btree_map_ext_removes() {
let mut m: dequemap::DequeBTreeMap<_, _> = dequemap::DequeBTreeMap::default();
m.push_back(3, TimedValue::new((), Some(Duration::from_millis(800))));
std::thread::sleep(Duration::from_millis(100));
m.push_back(1, TimedValue::new((), Some(Duration::from_millis(800))));
std::thread::sleep(Duration::from_millis(100));
m.push_back(6, TimedValue::new((), Some(Duration::from_millis(800))));
std::thread::sleep(Duration::from_millis(100));
m.push_back(8, TimedValue::new((), Some(Duration::from_millis(800))));
std::thread::sleep(Duration::from_millis(100));
m.push_back(3, TimedValue::new((), Some(Duration::from_millis(800))));
assert_eq!(m.len(), 4);
for (key, item) in m.iter() {
println!("key: {:?}, is_expired: {}", key, item.is_expired());
}
println!("--------------------------------------------------------------");
std::thread::sleep(Duration::from_millis(600));
while let Some((key, item)) = m.front() {
println!("clean expired, key: {}", key);
if item.is_expired() {
m.pop_front();
} else {
break;
}
}
println!("m.len(): {}", m.len());
for (key, item) in m.iter() {
println!("key: {:?}, is_expired: {}", key, item.is_expired());
}
assert_eq!(m.len(), 2);
}