use std::fmt;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ShutdownReport {
pub exited_clean: Vec<&'static str>,
pub laggards: Vec<LaggardReport>,
pub total: Duration,
}
impl ShutdownReport {
pub fn is_clean(&self) -> bool {
self.laggards.is_empty()
}
pub fn loop_count(&self) -> usize {
self.exited_clean.len() + self.laggards.len()
}
}
impl fmt::Display for ShutdownReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"shutdown: {} clean / {} laggards / {:?}",
self.exited_clean.len(),
self.laggards.len(),
self.total
)?;
for l in &self.laggards {
write!(f, "\n {l}")?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct LaggardReport {
pub name: &'static str,
pub uptime: Duration,
pub wait_elapsed: Duration,
pub aborted: bool,
}
impl fmt::Display for LaggardReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"laggard \"{}\" uptime={:?} wait={:?} aborted={}",
self.name, self.uptime, self.wait_elapsed, self.aborted
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_formats_clean_report() {
let r = ShutdownReport {
exited_clean: vec!["a", "b"],
laggards: vec![],
total: Duration::from_millis(12),
};
let s = r.to_string();
assert!(s.contains("2 clean"));
assert!(s.contains("0 laggards"));
assert!(r.is_clean());
assert_eq!(r.loop_count(), 2);
}
#[test]
fn display_formats_laggards() {
let r = ShutdownReport {
exited_clean: vec!["a"],
laggards: vec![LaggardReport {
name: "wal_catchup",
uptime: Duration::from_secs(2),
wait_elapsed: Duration::from_millis(900),
aborted: true,
}],
total: Duration::from_millis(900),
};
let s = r.to_string();
assert!(s.contains("1 clean"));
assert!(s.contains("1 laggards"));
assert!(s.contains("wal_catchup"));
assert!(s.contains("uptime=2s"));
assert!(s.contains("wait=900ms"));
assert!(s.contains("aborted=true"));
assert!(!r.is_clean());
assert_eq!(r.loop_count(), 2);
}
}