use std::{
collections::HashMap,
time::{Duration, SystemTime},
};
pub struct TimedSet<T> {
ttl: Duration,
set: HashMap<T, SystemTime>,
}
impl<T> TimedSet<T>
where
T: std::hash::Hash + Eq,
{
pub fn new(ttl: Duration) -> Self {
Self {
ttl,
set: HashMap::new(),
}
}
pub fn add(&mut self, val: T) {
self.set.insert(val, SystemTime::now() + self.ttl);
}
pub fn add_with_ttl(&mut self, val: T, ttl: Duration) {
self.set.insert(val, SystemTime::now() + ttl);
}
pub fn contains(&self, val: &T) -> bool {
if let Some(s) = self.set.get(val) {
if SystemTime::now() < *s {
return true;
}
}
false
}
pub fn iter(&self) -> Iter<'_, T> {
Iter {
set: self.set.iter().map(|(k, v)| (k, v)).collect(),
}
}
}
pub struct Iter<'a, T> {
set: HashMap<&'a T, &'a SystemTime>,
}
impl<'a, T> Iterator for Iter<'a, T>
where
T: Copy + std::hash::Hash + Eq,
{
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let keys: Vec<&T> = self.set.keys().cloned().collect();
for k in keys {
if let Some((v, t)) = self.set.remove_entry(&k) {
if SystemTime::now() < *t {
return Some(v);
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timedset_with_ttl_str() {
let mut ts = TimedSet::new(Duration::from_secs(2));
ts.add("element_1");
ts.add("element_2");
assert!(ts.contains(&"element_1"));
assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(ts.contains(&"element_1"));
assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(!ts.contains(&"element_1"));
assert!(!ts.contains(&"element_2"));
}
#[test]
fn test_timedset_with_ttl_string() {
let mut ts = TimedSet::new(Duration::from_secs(2));
ts.add("element_1".to_string());
ts.add("element_2".to_string());
assert!(ts.contains(&"element_1".to_string()));
assert!(ts.contains(&"element_2".to_string()));
std::thread::sleep(Duration::from_secs(1));
assert!(ts.contains(&"element_1".to_string()));
assert!(ts.contains(&"element_2".to_string()));
std::thread::sleep(Duration::from_secs(1));
assert!(!ts.contains(&"element_1".to_string()));
assert!(!ts.contains(&"element_2".to_string()));
}
#[test]
fn test_timedset_str() {
let mut ts = TimedSet::new(Duration::from_secs(0));
ts.add_with_ttl("element_1", Duration::from_secs(2));
ts.add_with_ttl("element_2", Duration::from_secs(3));
assert!(ts.contains(&"element_1"));
assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(ts.contains(&"element_1"));
assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(!ts.contains(&"element_1"));
assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(!ts.contains(&"element_1"));
assert!(!ts.contains(&"element_2"));
}
#[test]
fn test_timedset_mix() {
let mut ts = TimedSet::new(Duration::from_secs(2));
ts.add("element_1");
ts.add_with_ttl("element_2", Duration::from_secs(3));
assert!(ts.contains(&"element_1"));
assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(ts.contains(&"element_1"));
assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(!ts.contains(&"element_1")); assert!(ts.contains(&"element_2"));
std::thread::sleep(Duration::from_secs(1));
assert!(!ts.contains(&"element_1")); assert!(!ts.contains(&"element_2")); }
}