use super::guard::GuardInfo;
use std::{
collections::BTreeMap,
fmt::{self, Display, Formatter},
panic::Location,
sync::Arc,
time::Duration,
};
#[derive(Debug, Clone)]
pub struct GuardReport {
entries: Vec<GuardReportEntry>,
}
#[derive(Debug, Clone, Copy)]
pub struct GuardReportEntry {
location: &'static Location<'static>,
count: usize,
oldest_age: Option<Duration>,
}
impl GuardReport {
pub(crate) fn from_infos(infos: Vec<Arc<GuardInfo>>) -> Self {
let mut by_location: BTreeMap<&'static Location<'static>, GuardReportEntry> =
BTreeMap::new();
for info in infos {
let entry = by_location
.entry(info.location())
.or_insert_with(|| GuardReportEntry {
location: info.location(),
count: 0,
oldest_age: None,
});
entry.count += 1;
entry.oldest_age = entry.oldest_age.max(info.age());
}
let mut entries: Vec<_> = by_location.into_values().collect();
entries.sort_by(|a, b| {
b.oldest_age
.cmp(&a.oldest_age)
.then_with(|| a.location.cmp(b.location))
});
Self { entries }
}
#[must_use]
pub fn guard_count(&self) -> usize {
self.entries.iter().map(GuardReportEntry::count).sum()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn entries(&self) -> &[GuardReportEntry] {
&self.entries
}
pub fn iter(&self) -> std::slice::Iter<'_, GuardReportEntry> {
self.entries.iter()
}
}
impl GuardReportEntry {
#[must_use]
pub fn location(&self) -> &'static Location<'static> {
self.location
}
#[must_use]
pub fn count(&self) -> usize {
self.count
}
#[must_use]
pub fn oldest_age(&self) -> Option<Duration> {
self.oldest_age
}
}
impl IntoIterator for GuardReport {
type Item = GuardReportEntry;
type IntoIter = std::vec::IntoIter<GuardReportEntry>;
fn into_iter(self) -> Self::IntoIter {
self.entries.into_iter()
}
}
impl<'a> IntoIterator for &'a GuardReport {
type Item = &'a GuardReportEntry;
type IntoIter = std::slice::Iter<'a, GuardReportEntry>;
fn into_iter(self) -> Self::IntoIter {
self.entries.iter()
}
}
impl Display for GuardReport {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let count = self.guard_count();
if count == 0 {
return f.write_str("no outstanding guards");
}
let plural = if count == 1 { "" } else { "s" };
write!(f, "{count} outstanding guard{plural}:")?;
for entry in &self.entries {
write!(f, "\n{entry}")?;
}
Ok(())
}
}
impl Display for GuardReportEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} × {}", self.count, self.location)?;
if let Some(age) = self.oldest_age {
write!(f, " (oldest {age:.1?})")?;
}
Ok(())
}
}