From 7762bdad7535596b76dc7081770a4bec6b0cb50c Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Tue, 7 Jan 2025 02:02:03 +0100 Subject: [PATCH] fix(analyze): outages were not displayed #34 --- src/analyze.rs | 96 ++++++++++++++++++++++++++++++-------------------- src/records.rs | 4 +++ 2 files changed, 61 insertions(+), 39 deletions(-) diff --git a/src/analyze.rs b/src/analyze.rs index 2ea9168..6dafdbc 100644 --- a/src/analyze.rs +++ b/src/analyze.rs @@ -31,9 +31,10 @@ use chrono::{DateTime, Local}; use deepsize::DeepSizeOf; +use tracing::error; use crate::errors::AnalysisError; -use crate::records::{Check, CheckType, IpType}; +use crate::records::{display_group, indented_check, Check, CheckType, IpType}; use crate::store::Store; use std::fmt::{Display, Write}; @@ -85,38 +86,57 @@ impl<'check> Outage<'check> { ) -> Self { Self { start, - end, + end: if Some(start) == end { None } else { end }, all: all_checks.to_vec(), } } + + /// Returns the length of this [`Outage`]. + pub fn len(&self) -> usize { + self.all.len() + } + + /// Returns true if this [`Outage`] is empty. + #[must_use] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } impl Display for Outage<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut buf: String = String::new(); if self.end.is_some() { - writeln!( - f, - "From {} To {}", - fmt_timestamp(self.start.timestamp_parsed()), - fmt_timestamp(self.end.unwrap().timestamp_parsed()) + key_value_write( + &mut buf, + "From", + fmt_timestamp(self.end.unwrap().timestamp_parsed()), + )?; + key_value_write( + &mut buf, + "To", + fmt_timestamp(self.end.unwrap().timestamp_parsed()), )?; } else { - writeln!( - f, - "From {} STILL ONGOING", + key_value_write( + &mut buf, + "From", fmt_timestamp(self.start.timestamp_parsed()), )?; + key_value_write(&mut buf, "To", "(None)")?; } - writeln!(f, "Checks: {}", self.all.len())?; - writeln!( - f, - "Type: {}", - self.start.calc_type().unwrap_or(CheckType::Unknown) - )?; + key_value_write(&mut buf, "Total", self.len())?; + writeln!(buf, "\nDetails")?; + display_group(&self.all, &mut buf)?; + write!(f, "{buf}")?; Ok(()) } } +fn more_indent(buf: &str) -> String { + format!("\t{}", buf.to_string().replace("\n", "\n\t")) +} + /// Generate a comprehensive analysis report for the given store. /// /// The report includes: @@ -205,44 +225,42 @@ fn key_value_write( writeln!(f, "{:<24}: {}", title, content) } +/// Writes a key-value pair to the report in aligned columns. +/// +/// Format: `: ` +fn key_value_write_indented( + f: &mut String, + title: &str, + content: impl Display, +) -> Result<(), std::fmt::Error> { + writeln!(f, " {:<16}: {}", title, content) +} + /// Analyzes and formats outage information from the store. /// /// Groups consecutive failed checks by check type and creates /// Outage records for reporting. fn outages(store: &Store, f: &mut String) -> Result<(), AnalysisError> { let all_checks: Vec<&Check> = store.checks().iter().collect(); - let mut outages: Vec = Vec::new(); - let fails_exist = all_checks - .iter() - .fold(true, |fails_exist, c| fails_exist & !c.is_success()); + let fails_exist = !all_checks.iter().all(|c| c.is_success()); if !fails_exist || all_checks.is_empty() { writeln!(f, "None\n")?; return Ok(()); } - for check_type in CheckType::all() { - let checks: Vec<&&Check> = all_checks - .iter() - .filter(|c| c.calc_type().unwrap_or(CheckType::Unknown) == *check_type) - .collect(); + let failed_checks: Vec<&&Check> = all_checks.iter().filter(|c| !c.is_success()).collect(); - let fail_groups = fail_groups(&checks); - for group in fail_groups { - // writeln!(f, "Group {gidx}:")?; - // display_group(group, f)?; - if !group.is_empty() { - outages.push(Outage::new( - checks.first().unwrap(), - Some(checks.last().unwrap()), - &group, - )); - } + let fail_groups = fail_groups(&failed_checks); + for (outage_idx, group) in fail_groups.into_iter().enumerate() { + if group.is_empty() { + error!("empty outage group"); + continue; } + let outage = Outage::new(group.first().unwrap(), group.last().copied(), &group); + writeln!(f, "{outage_idx}:\n{}", more_indent(&outage.to_string()))?; } + writeln!(f)?; - for outage in outages { - writeln!(f, "{outage}")?; - } Ok(()) } diff --git a/src/records.rs b/src/records.rs index 4de1481..aa1ea57 100644 --- a/src/records.rs +++ b/src/records.rs @@ -462,6 +462,10 @@ pub fn display_group(group: &[&Check], f: &mut String) -> Result<(), std::fmt::E Ok(()) } +pub(crate) fn indented_check(buf: &mut String, check: &Check) -> Result<(), std::fmt::Error> { + writeln!(buf, "\t{}", check.to_string().replace("\n", "\n\t")) +} + #[cfg(test)] mod test { use crate::TIMEOUT_MS;