use std::collections::HashMap;
use std::hash::Hash;
#[derive(Clone, Debug)]
pub struct Told<Viewer: Eq + Hash, K: Eq + Hash + Ord + Copy, V: PartialEq> {
known: HashMap<Viewer, HashMap<K, V>>,
}
impl<Viewer: Eq + Hash, K: Eq + Hash + Ord + Copy, V: PartialEq> Default for Told<Viewer, K, V> {
fn default() -> Self {
Self::new()
}
}
impl<Viewer: Eq + Hash, K: Eq + Hash + Ord + Copy, V: PartialEq> Told<Viewer, K, V> {
pub fn new() -> Self {
Self { known: HashMap::new() }
}
pub fn diff(
&mut self,
viewer: Viewer,
current: impl IntoIterator<Item = (K, V)>,
mut say: impl FnMut(K, Option<&V>),
) {
let known = self.known.entry(viewer).or_default();
let mut next: HashMap<K, V> = HashMap::with_capacity(known.len());
for (key, value) in current {
if known.get(&key) != Some(&value) {
say(key, Some(&value));
}
known.remove(&key);
next.insert(key, value);
}
let mut gone: Vec<K> = known.keys().copied().collect();
gone.sort_unstable();
for key in gone {
say(key, None);
}
*known = next;
}
pub fn forget(&mut self, viewer: &Viewer) {
self.known.remove(viewer);
}
pub fn holdings(&self, viewer: &Viewer) -> usize {
self.known.get(viewer).map_or(0, HashMap::len)
}
pub fn viewers(&self) -> usize {
self.known.len()
}
pub fn clear(&mut self) {
self.known.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
fn said(told: &mut Told<u32, u32, u8>, viewer: u32, current: &[(u32, u8)]) -> (Vec<(u32, u8)>, Vec<u32>) {
let (mut updated, mut gone) = (Vec::new(), Vec::new());
told.diff(viewer, current.iter().copied(), |key, value| match value {
Some(v) => updated.push((key, *v)),
None => gone.push(key),
});
(updated, gone)
}
#[test]
fn a_still_world_is_said_once() {
let mut told = Told::new();
let world = [(1u32, 7u8), (2, 9)];
let (updated, gone) = said(&mut told, 5, &world);
assert_eq!(updated, vec![(1, 7), (2, 9)], "a fresh viewer hears everything");
assert!(gone.is_empty());
let (updated, gone) = said(&mut told, 5, &world);
assert!(updated.is_empty(), "and then nothing while nothing moves");
assert!(gone.is_empty());
}
#[test]
fn a_change_is_said_and_the_rest_stays_quiet() {
let mut told = Told::new();
said(&mut told, 5, &[(1, 7), (2, 9)]);
let (updated, _) = said(&mut told, 5, &[(1, 7), (2, 10)]);
assert_eq!(updated, vec![(2, 10)]);
}
#[test]
fn each_viewer_has_their_own_memory() {
let mut told = Told::new();
said(&mut told, 5, &[(1, 7)]);
let (updated, _) = said(&mut told, 6, &[(1, 7)]);
assert_eq!(updated, vec![(1, 7)], "a joiner hears what the veteran already knows");
}
#[test]
fn a_key_that_leaves_is_handed_back_and_returns_as_new() {
let mut told = Told::new();
said(&mut told, 5, &[(1, 7), (2, 9)]);
let (updated, gone) = said(&mut told, 5, &[(1, 7)]);
assert!(updated.is_empty());
assert_eq!(gone, vec![2]);
let (updated, _) = said(&mut told, 5, &[(1, 7), (2, 9)]);
assert_eq!(updated, vec![(2, 9)], "back means said again");
}
#[test]
fn announce_once_is_the_unit_value_case() {
let mut told: Told<u32, u32, ()> = Told::new();
let (mut first, mut second) = (Vec::new(), Vec::new());
told.diff(5, [(40, ()), (41, ())], |key, value| {
if value.is_some() {
first.push(key);
}
});
told.diff(5, [(40, ()), (41, ()), (42, ())], |key, value| {
if value.is_some() {
second.push(key);
}
});
assert_eq!(first, vec![40, 41]);
assert_eq!(second, vec![42], "only the new spawn is announced");
}
#[test]
fn a_forgotten_viewer_hears_everything_again() {
let mut told = Told::new();
said(&mut told, 5, &[(1, 7)]);
told.forget(&5);
let (updated, _) = said(&mut told, 5, &[(1, 7)]);
assert_eq!(updated, vec![(1, 7)]);
}
}