Skip to content

Commit

Permalink
docs(outage): add api documentation for whole module
Browse files Browse the repository at this point in the history
  • Loading branch information
PlexSheep committed Jan 8, 2025
1 parent 39d79d7 commit 9f282ce
Showing 1 changed file with 106 additions and 13 deletions.
119 changes: 106 additions & 13 deletions src/analyze/outage.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
//! Analysis and tracking of network outage periods.
//!
//! This module provides types and functions for analyzing periods of failed network checks:
//! - [`Outage`] - Represents a period of consecutive failed checks
//! - [`Severity`] - Classifies outage impact (complete, partial, none)
//!
//! # Outage Analysis
//!
//! An outage is defined as a period containing one or more failed network checks. The module helps:
//! - Track start/end times of outages
//! - Calculate outage severity/impact
//! - Generate outage reports and statistics
use std::cmp::Ordering;
use std::fmt::Display;
use std::fmt::Write;
Expand All @@ -9,13 +22,39 @@ use crate::records::Check;

use super::{fmt_timestamp, key_value_write, CheckGroup};

/// Error returned when trying to create a [`Severity`] from an invalid value.
///
/// Returned when attempting to create a [`Severity`] from a value greater than 1.0
/// since severity is represented as a percentage between 0.0 and 1.0.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct FromRawSeverityError(f64);

/// Classification of outage impact severity.
///
/// Represents how severely network connectivity was impacted during an outage period:
/// - Complete (only failed checks)
/// - Partial (some failed checks)
/// - None (no failed checks)
///
/// # Examples
///
/// ```rust
/// use netpulse::analyze::outage::Severity;
///
/// let complete = Severity::try_from(1.0).unwrap();
/// let partial = Severity::try_from(0.5).unwrap();
/// let none = Severity::try_from(0.0).unwrap();
///
/// assert!(complete > partial);
/// assert!(partial > none);
/// ```
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Severity {
/// All checks failed (100% failure rate)
Complete,
/// Some checks failed (partial failure rate between 0-100%)
Partial(f64),
/// No checks failed (0% failure rate)
None,
}

Expand Down Expand Up @@ -61,27 +100,50 @@ impl Display for Severity {
}
}

/// Represents a period of consecutive failed checks.
/// Represents a period of consecutive failed network checks.
///
/// An outage is defined by:
/// - A start check that failed
/// - An optional end check (None if outage is ongoing)
/// - All failed checks during the outage period
/// - One or more consecutive checks that failed
/// - An outage with no checks is technically allowed, but serves no purpose
///
/// From that, we can extrapolate:
/// - A start time (first failed check)
/// - An end time (last failed check)
/// - A severity classification
///
/// # Examples
///
/// ```rust,no_run
/// use netpulse::records::Check;
/// use netpulse::analyze::outage::Outage;
///
/// # let checks = vec![];
/// let outage = Outage::new(&checks);
///
/// This struct helps track and analyze network connectivity issues
/// over time.
/// println!("Outage report:\n{}", outage.short_report().unwrap());
/// ```
#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
pub struct Outage<'check> {
/// All checks that failed during this outage period
/// All checks that occurred during this outage period
all: Vec<&'check Check>,
}

impl<'check> Outage<'check> {
/// Creates a new outage from its constituent checks.
/// Creates a new outage from a slice of checks.
///
/// # Arguments
///
/// * `all_checks` - Slice of all failed checks in this period
/// * `all_checks` - Slice of all checks in this period (both failed and successful)
///
/// # Examples
///
/// ```rust
/// use netpulse::records::Check;
/// use netpulse::analyze::outage::Outage;
///
/// # let checks = vec![];
/// let outage = Outage::new(&checks);
/// ```
pub fn new(all_checks: &[&'check Check]) -> Self {
if all_checks.is_empty() {
warn!("created an empty outage");
Expand All @@ -96,17 +158,27 @@ impl<'check> Outage<'check> {
&self.all
}

/// Returns the last [Check] of the [Outage], or `None` if it is empty.
/// Returns the last [Check] of the [Outage], or [`None`] if it is empty.
pub fn last(&self) -> Option<&Check> {
self.all.last().copied()
}

/// Returns the first [Check] of the [Outage], or `None` if it is empty.
/// Returns the first [Check] of the [Outage], or [`None`] if it is empty.
pub fn first(&self) -> Option<&Check> {
self.all.first().copied()
}

/// Display information about that [Outage] in a short format
/// Generates a concise single-line report of the outage.
///
/// The report includes:
/// - Start time
/// - End time
/// - Total number of checks
/// - Severity classification
///
/// # Errors
///
/// Returns [`std::fmt::Error`] if string formatting fails.
pub fn short_report(&self) -> Result<String, std::fmt::Error> {
if self.is_empty() {
error!("Outage does not contain any checks");
Expand All @@ -127,7 +199,7 @@ impl<'check> Outage<'check> {
Ok(buf)
}

/// Returns the length of this [`Outage`].
/// Returns the total number of checks in this outage period.
pub fn len(&self) -> usize {
self.all.len()
}
Expand All @@ -138,13 +210,34 @@ impl<'check> Outage<'check> {
self.len() == 0
}

/// Calculates the severity of this outage.
///
/// Severity is based on the percentage of failed checks:
/// - 100% = Complete outage
/// - 0% = No outage
/// - Other = Partial outage
///
/// # Examples
///
/// ```rust,no_run
/// use netpulse::records::Check;
/// use netpulse::analyze::outage::Outage;
///
/// # let checks = vec![];
/// let outage = Outage::new(&checks);
/// println!("Severity: {}", outage.severity());
/// ```
pub fn severity(&self) -> Severity {
let all = self.all();
let percentage: f64 =
all.iter().filter(|a| !a.is_success()).count() as f64 / all.len() as f64;
Severity::try_from(percentage).expect("calculated more than 100% success")
}

/// Compares two outages by severity then by duration.
///
/// Orders outages first by severity (complete > partial > none),
/// then by number of checks for equal severities.
pub fn cmp_severity(&self, other: &Self) -> Ordering {
match self
.severity()
Expand Down

0 comments on commit 9f282ce

Please sign in to comment.