use std::{collections::BTreeMap, sync::Arc, time::Instant};
use crate::core::models::mac::{self, MacAddr};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HardwareInfo {
pub(crate) macs: BTreeMap<MacAddr, Instant>,
pub vendor: Option<Arc<str>>,
}
impl HardwareInfo {
pub fn new(mac: MacAddr) -> Self {
let mut macs = BTreeMap::new();
macs.insert(mac, Instant::now());
Self {
macs,
vendor: mac::vendor(&mac).map(Arc::from),
}
}
pub fn add_mac(&mut self, mac: MacAddr) {
self.macs.insert(mac, Instant::now());
if self.vendor.is_none() {
self.vendor = mac::vendor(&mac).map(Arc::from);
}
}
#[inline]
pub fn macs(&self) -> &BTreeMap<MacAddr, Instant> {
&self.macs
}
pub fn most_recent_mac(&self) -> Option<MacAddr> {
self.macs
.iter()
.max_by_key(|&(_, time)| time)
.map(|(mac, _)| *mac)
}
pub fn prune_stale_macs(&mut self, cutoff: Instant) {
self.macs.retain(|_, last_seen| *last_seen >= cutoff);
}
pub fn merge(&mut self, other: HardwareInfo) {
for (mac, time) in other.macs {
self.macs
.entry(mac)
.and_modify(|t| {
if time > *t {
*t = time;
}
})
.or_insert(time);
}
if self.vendor.is_none() {
self.vendor = other.vendor;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn hardware_vendor_assignment() {
let mac = MacAddr::new(0x00, 0x0C, 0x29, 0xAB, 0xCD, 0xEF);
let mut hw = HardwareInfo::new(mac);
hw.vendor = Some(Arc::from("VMware, Inc."));
assert_eq!(hw.vendor.as_deref().unwrap(), "VMware, Inc.");
assert!(hw.macs().contains_key(&mac));
}
#[test]
fn test_most_recent_mac_selection() {
let mac_old = MacAddr::new(1, 1, 1, 1, 1, 1);
let mac_new = MacAddr::new(2, 2, 2, 2, 2, 2);
let mut hw = HardwareInfo::new(mac_old);
let future_time = Instant::now() + Duration::from_secs(60);
hw.macs.insert(mac_new, future_time);
assert_eq!(hw.most_recent_mac(), Some(mac_new));
}
#[test]
fn most_recent_mac_on_empty() {
let hw = HardwareInfo { macs: BTreeMap::new(), vendor: None };
assert_eq!(hw.most_recent_mac(), None);
}
#[test]
fn prune_stale_macs_logic() {
let mac_keep = MacAddr::new(1, 1, 1, 1, 1, 1);
let mac_drop = MacAddr::new(2, 2, 2, 2, 2, 2);
let mut hw = HardwareInfo::new(mac_keep);
let past_time = Instant::now() - Duration::from_secs(3600);
hw.macs.insert(mac_drop, past_time);
let cutoff = Instant::now() - Duration::from_secs(1800);
hw.prune_stale_macs(cutoff);
assert_eq!(hw.macs().len(), 1);
assert!(hw.macs().contains_key(&mac_keep));
}
}