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])))
}