use std::borrow::Borrow;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
pub struct HistoryMap<K, V> {
map: HashMap<K, V>,
history: Vec<(K, Option<V>)>,
}
impl<K, V> Default for HistoryMap<K, V> {
fn default() -> Self {
Self {
map: Default::default(),
history: Default::default(),
}
}
}
impl<K, V> FromIterator<(K, V)> for HistoryMap<K, V>
where
K: Eq + Hash,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
HistoryMap {
map: iter.into_iter().collect(),
history: Vec::new(),
}
}
}
impl<K, V> HistoryMap<K, V>
where
K: Hash + Eq + Clone,
V: Eq,
{
pub fn insert(&mut self, k: K, v: V) {
match self.map.entry(k) {
Entry::Vacant(e) => {
self.history.push((e.key().clone(), None));
e.insert(v);
}
Entry::Occupied(mut e) if e.get() != &v => {
self.history.push((e.key().clone(), Some(e.insert(v))));
}
_ => (),
}
}
pub fn get<Q>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.map.get(k)
}
pub fn get_or_try_insert_with<F, E>(&mut self, k: K, f: F) -> std::result::Result<&V, E>
where
F: FnOnce() -> std::result::Result<V, E>,
{
match self.map.entry(k) {
Entry::Occupied(e) => Ok(e.into_mut()),
Entry::Vacant(e) => {
let v = f()?;
self.history.push((e.key().clone(), None));
Ok(e.insert(v))
}
}
}
pub fn rollback(&mut self, height: usize) {
if self.history.len() <= height {
return;
}
for (k, v) in self.history.drain(height..).rev() {
match v {
Some(v) => self.map.insert(k, v),
None => self.map.remove(&k),
};
}
}
pub fn history_len(&self) -> usize {
self.history.len()
}
pub fn discard_history(&mut self) {
self.history.clear();
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
self.map.iter_mut()
}
}
#[cfg(test)]
mod test {
use super::HistoryMap;
#[test]
fn history_map() {
let mut map = HistoryMap::<i32, &'static str>::default();
assert_eq!(map.get(&1), None);
assert_eq!(map.history_len(), 0);
map.insert(1, "foo");
assert_eq!(map.history_len(), 1);
assert_eq!(map.get(&1), Some(&"foo"));
map.insert(2, "bar");
assert_eq!(map.history_len(), 2);
assert_eq!(map.get(&1), Some(&"foo"));
assert_eq!(map.get(&2), Some(&"bar"));
map.insert(1, "baz");
assert_eq!(map.history_len(), 3);
assert_eq!(map.get(&1), Some(&"baz"));
map.rollback(4); assert_eq!(map.history_len(), 3);
map.rollback(3); assert_eq!(map.history_len(), 3);
assert_eq!(map.get(&1), Some(&"baz"));
map.rollback(2); assert_eq!(map.history_len(), 2);
assert_eq!(map.get(&1), Some(&"foo"));
assert_eq!(map.get(&2), Some(&"bar"));
map.rollback(1); assert_eq!(map.history_len(), 1);
assert_eq!(map.get(&1), Some(&"foo"));
assert_eq!(map.get(&2), None);
map.rollback(0); assert_eq!(map.history_len(), 0);
assert_eq!(map.get(&1), None);
assert_eq!(
map.get_or_try_insert_with(1, || -> Result<_, ()> { Ok("foo") })
.unwrap(),
&"foo",
);
assert_eq!(map.get(&1), Some(&"foo"));
assert_eq!(map.history_len(), 1);
assert_eq!(
map.get_or_try_insert_with(1, || -> Result<_, ()> { panic!() })
.unwrap(),
&"foo",
);
assert_eq!(map.history_len(), 1);
map.insert(1, "foo");
assert_eq!(map.history_len(), 1);
assert_eq!(
map.get_or_try_insert_with(2, || { Err("err") })
.unwrap_err(),
"err",
);
assert_eq!(map.get(&2), None);
assert_eq!(map.history_len(), 1);
map.rollback(0); assert_eq!(map.history_len(), 0);
assert_eq!(map.get(&1), None);
}
}