Functional Weave
Code in Rust

monitor.mttr@1.0.0

impl/rust.rs

3,330 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_div::round_div;  ← from math.round-div ^1.0.0 · built alongside by fune
use super::monitor_incidents::{incidents_from_value, Incident};  ← from monitor.incidents ^1.0.0 · built alongside by fune

/// Reliability figures for the window from <= t < to. An incident counts when
/// it overlaps the window; downtime is clipped to it, but MTTR and the
/// longest incident use full durations, because an outage that began last
/// month still took as long as it took to fix.
///
/// # Panics
/// Panics when from is after to, on an inconsistent incident, or on
/// incidents out of order or overlapping.
pub fn reliability_stats(incidents: &[Incident], from: i64, to: i64) -> ReliabilityStats {
    if from > to {
        panic!("from must not be after to: {} > {}", from, to);
    }
    let mut prev_end: Option<i64> = None;
    let (mut count, mut resolved, mut downtime, mut repair_total, mut longest) = (0i64, 0i64, 0i64, 0i64, 0i64);
    for x in incidents {
        if x.duration_seconds < 0 {
            panic!("durationSeconds must not be negative, received {}", x.duration_seconds);
        }
        if let Some(e) = x.end {
            if e < x.start {
                panic!("incident end must not be before its start: {} < {}", e, x.start);
            }
            if x.duration_seconds != e - x.start {
                panic!("durationSeconds must equal end - start: {} != {}", x.duration_seconds, e - x.start);
            }
        }
        // An ongoing incident ends, for now, where its duration says.
        let end = x.start + x.duration_seconds;
        if let Some(p) = prev_end {
            if x.start < p {
                panic!("incidents must be in time order and must not overlap: {} is before {}", x.start, p);
            }
        }
        prev_end = Some(end);
        if !(x.start < to && (x.start >= from || end > from)) {
            continue;
        }
        count += 1;
        downtime += (end.min(to) - x.start.max(from)).max(0);
        if x.end.is_some() {
            resolved += 1;
            repair_total += x.duration_seconds;
        }
        longest = longest.max(x.duration_seconds);
    }
    ReliabilityStats {
        incidents: count,
        resolved,
        downtime_seconds: downtime,
        mttr_seconds: if resolved > 0 { Some(round_div(repair_total, resolved, "half-up")) } else { None },
        mtbf_seconds: if count > 0 { Some(round_div(to - from - downtime, count, "half-up")) } else { None },
        longest_seconds: longest,
    }
}

fn opt_to_value(v: Option<i64>) -> Value {
    match v {
        Some(i) => Value::Int(i),
        None => Value::Null,
    }
}

pub fn reliability_stats_to_value(r: &ReliabilityStats) -> Value {
    Value::obj(vec![
        ("incidents", Value::Int(r.incidents)),
        ("resolved", Value::Int(r.resolved)),
        ("downtimeSeconds", Value::Int(r.downtime_seconds)),
        ("mttrSeconds", opt_to_value(r.mttr_seconds)),
        ("mtbfSeconds", opt_to_value(r.mtbf_seconds)),
        ("longestSeconds", Value::Int(r.longest_seconds)),
    ])
}

fn whole(v: &Value) -> i64 {
    match v {
        Value::Int(i) => *i,
        _ => panic!("from and to must be whole seconds"),
    }
}

pub fn fune_vector(args: &[Value]) -> Value {
    let incidents = incidents_from_value(&args[0]);
    reliability_stats_to_value(&reliability_stats(&incidents, whole(&args[1]), whole(&args[2])))
}