use std::collections::{BTreeMap, BTreeSet};
use std::time::Instant;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct NetworkRow {
pub(crate) network: u16,
pub(crate) routers: Vec<String>,
pub(crate) heard: Instant,
}
impl NetworkRow {
pub(crate) fn duplicate(&self) -> bool {
self.routers.len() > 1
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct RouterMap {
routers: BTreeMap<String, BTreeMap<u16, Instant>>,
}
impl RouterMap {
pub(crate) fn record(&mut self, router: &str, networks: &[u16], at: Instant) {
let entry = self.routers.entry(router.to_string()).or_default();
for &network in networks {
entry.insert(network, at);
}
}
pub(crate) fn router_count(&self) -> usize {
self.routers.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.routers.is_empty()
}
pub(crate) fn rows(&self) -> Vec<NetworkRow> {
let mut rows: BTreeMap<u16, NetworkRow> = BTreeMap::new();
for (router, networks) in &self.routers {
for (&network, &at) in networks {
let row = rows.entry(network).or_insert_with(|| NetworkRow {
network,
routers: Vec::new(),
heard: at,
});
row.routers.push(router.clone());
row.heard = row.heard.max(at);
}
}
rows.into_values().collect()
}
pub(crate) fn duplicates(&self) -> BTreeSet<u16> {
self.rows()
.into_iter()
.filter(NetworkRow::duplicate)
.map(|row| row.network)
.collect()
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
#[test]
fn networks_map_to_their_routers_and_duplicates_are_flagged() {
let t0 = Instant::now();
let later = t0 + Duration::from_secs(5);
let mut map = RouterMap::default();
assert!(map.is_empty());
map.record("10.0.0.1:47808", &[2, 3], t0);
map.record("10.0.0.9:47808", &[4], t0);
assert_eq!(map.duplicates(), BTreeSet::new());
map.record("10.0.0.7:47808", &[3], later);
map.record("10.0.0.1:47808", &[2], later);
assert_eq!(map.router_count(), 3);
let rows = map.rows();
let networks: Vec<u16> = rows.iter().map(|row| row.network).collect();
assert_eq!(networks, [2, 3, 4]);
assert_eq!(rows[0].routers, ["10.0.0.1:47808"]);
assert_eq!(rows[0].heard, later);
assert!(!rows[0].duplicate());
assert_eq!(rows[1].routers, ["10.0.0.1:47808", "10.0.0.7:47808"]);
assert!(rows[1].duplicate());
assert_eq!(rows[1].heard, later);
assert_eq!(rows[2].heard, t0);
assert_eq!(map.duplicates(), BTreeSet::from([3]));
}
#[test]
fn a_router_heard_twice_is_one_router() {
let t0 = Instant::now();
let mut map = RouterMap::default();
map.record("02", &[7], t0);
map.record("02", &[7, 8], t0);
assert_eq!(map.router_count(), 1);
assert_eq!(map.duplicates(), BTreeSet::new());
assert_eq!(map.rows().len(), 2);
}
}