use indexmap::IndexMap;
use std::hash::Hash;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone)]
pub struct ExpireMap<K, V, T>
where
K: Eq + Hash + Clone,
{
map: IndexMap<K, ExpiryEntry<V, T>>,
}
impl<K, V, T> Default for ExpireMap<K, V, T>
where
K: Eq + Hash + Clone,
{
fn default() -> Self {
Self {
map: IndexMap::default(),
}
}
}
impl<K, V, T> ExpireMap<K, V, T>
where
K: Eq + Hash + Clone,
{
pub fn new() -> Self {
Self {
map: IndexMap::new(),
}
}
}
impl<K, V, T> ExpireMap<K, V, T>
where
K: Eq + Hash + Clone,
T: Ord,
{
pub fn insert(&mut self, key: K, value: V, expiry: T) -> Option<ExpiryEntry<V, T>> {
let insert_idx = self.map.partition_point(|_, entry| entry.expiry < expiry);
let entry = ExpiryEntry::new(value, expiry);
self.map.insert_before(insert_idx, key, entry).1
}
pub fn get(&self, key: &K) -> Option<&V> {
self.map.get(key).map(|x| &x.value)
}
pub fn expire<'a>(&'a mut self, expiry: &T) -> ExpiredIterator<'a, K, ExpiryEntry<V, T>> {
let expiry_index = self.map.partition_point(|_, entry| entry.expiry <= *expiry);
ExpiredIterator(self.map.drain(0..expiry_index))
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn remove(&mut self, key: &K) -> Option<V> {
self.map.shift_remove(key).map(|entry| entry.value)
}
pub fn into_values(self) -> ValuesIterator<K, V, T> {
ValuesIterator(self.map.into_values())
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ExpiryEntry<V, T> {
pub value: V,
pub expiry: T,
}
impl<V, T> ExpiryEntry<V, T> {
fn new(value: V, expiry: T) -> Self {
Self { value, expiry }
}
}
pub struct ExpiredIterator<'a, K, V>(indexmap::map::Drain<'a, K, V>);
impl<K, V> Iterator for ExpiredIterator<'_, K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub struct ValuesIterator<K, V, T>(indexmap::map::IntoValues<K, ExpiryEntry<V, T>>);
impl<K, V, T> Iterator for ValuesIterator<K, V, T> {
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|x| x.value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_and_get() {
let mut map = ExpireMap::new();
map.insert("key", "HelloWorld", 42);
let value = map.get(&"key");
assert!(matches!(value, Some(&"HelloWorld")))
}
#[test]
fn insert_retrieves() {
let mut map = ExpireMap::new();
map.insert("key", "HelloWorld", 42);
let prev = map.insert("key", "HelloMom", 11);
assert!(matches!(
prev,
Some(ExpiryEntry {
value: "HelloWorld",
expiry: 42
})
))
}
#[test]
fn get_after_expired() {
let mut map = ExpireMap::new();
map.insert("key", "HelloWorld", 42);
map.expire(&42);
assert!(map.get(&"key").is_none())
}
#[test]
fn get_not_expired() {
let mut map = ExpireMap::new();
map.insert("key", "HelloWorld", 42);
map.expire(&41);
assert!(map.get(&"key").is_some())
}
#[test]
fn expiry_insertion_order() {
let mut map = ExpireMap::new();
map.insert("key0", "HelloWorld", 5);
map.insert("key1", "HelloWorld", 2);
map.insert("key2", "HelloWorld", 7);
map.insert("key3", "HelloWorld", -22);
map.expire(&4);
for k in ["key1", "key3"] {
assert!(map.get(&k).is_none())
}
for k in ["key0", "key2"] {
assert!(map.get(&k).is_some())
}
}
#[test]
fn expired_as_iterator() {
let mut map = ExpireMap::new();
let vals = ["foo", "bar", "baz"];
for (i, x) in vals.into_iter().enumerate() {
map.insert(x, x, i);
}
let expired: Vec<(&str, ExpiryEntry<&str, usize>)> = map.expire(&3).collect();
let expected: Vec<(&str, ExpiryEntry<&str, usize>)> = vals
.into_iter()
.enumerate()
.map(|(i, x)| (x, ExpiryEntry::new(x, i)))
.collect();
assert_eq!(expired, expected)
}
#[test]
fn is_empty() {
let mut map = ExpireMap::new();
assert!(map.is_empty());
let vals = ["foo", "bar", "baz"];
for (i, x) in vals.into_iter().enumerate() {
map.insert(x, x, i);
}
assert!(!map.is_empty());
let _ = map.expire(&1);
assert!(!map.is_empty());
let _ = map.expire(&2);
assert!(map.is_empty());
}
#[test]
fn len() {
let mut map = ExpireMap::new();
assert_eq!(0, map.len());
let vals = ["foo", "bar", "baz"];
for (i, x) in vals.into_iter().enumerate() {
map.insert(x, x, i);
}
assert_eq!(3, map.len());
let _ = map.expire(&1);
assert_eq!(1, map.len());
let _ = map.expire(&2);
assert_eq!(0, map.len());
}
#[test]
fn test_remove() {
let mut map = ExpireMap::new();
let vals = ["foo", "bar", "baz"];
for (i, x) in vals.into_iter().enumerate() {
map.insert(x, x, i);
}
let entry = map.remove(&"foo");
assert_eq!(entry.unwrap(), "foo");
assert!(map.remove(&"doesnotexist").is_none());
}
#[test]
fn test_remove_expiry() {
let mut map = ExpireMap::new();
assert_eq!(0, map.len());
let vals = ["foo", "bar", "baz"];
for (i, x) in vals.into_iter().enumerate() {
map.insert(x, x, i);
}
map.remove(&"bar");
let _ = map.expire(&1);
assert_eq!(1, map.len());
}
#[test]
fn test_into_values() {
let mut map = ExpireMap::new();
assert_eq!(0, map.len());
let vals = [("foo", "FOO"), ("bar", "BAR"), ("baz", "BAZ")];
for (i, x) in vals.into_iter().enumerate() {
map.insert(x.0, x.1, i);
}
let values: Vec<_> = map.into_values().collect();
assert_eq!(values, vec!["FOO", "BAR", "BAZ"]);
}
#[test]
fn test_expired_iterator_drop_removes() {
let mut map = ExpireMap::new();
let vals = [("foo", "FOO"), ("bar", "BAR"), ("baz", "BAZ")];
for (i, x) in vals.into_iter().enumerate() {
map.insert(x.0, x.1, i);
}
let it = map.expire(&usize::MAX);
drop(it);
assert!(map.is_empty())
}
}