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::monitor_check_status::{checks_from_value, Check}; ← from monitor.check-status ^1.0.0 · built alongside by fune
/// Seconds up, degraded, down and unknown over [from, to). Each check's status
/// holds from its `at` until the next check, but for at most max_gap_seconds:
/// a monitor that stopped reporting has not proved the target was up.
///
/// # Panics
/// Panics when from is after to, max_gap_seconds is below 1, the checks are
/// not strictly ascending, or a status is unknown.
pub fn uptime(checks: &[Check], from: i64, to: i64, max_gap_seconds: i64) -> UptimeReport {
if from > to {
panic!("from must not be after to: {} > {}", from, to);
}
if max_gap_seconds < 1 {
panic!("maxGapSeconds must be a whole number of at least 1, received {}", max_gap_seconds);
}
let (mut up, mut degraded, mut down) = (0i64, 0i64, 0i64);
let (mut count, mut down_count) = (0i64, 0i64);
for (i, c) in checks.iter().enumerate() {
if c.status != "up" && c.status != "degraded" && c.status != "down" {
panic!("unknown check status: {}", c.status);
}
if i > 0 && c.at <= checks[i - 1].at {
panic!("checks must be in strictly ascending time order: {} follows {}", c.at, checks[i - 1].at);
}
if c.at >= from && c.at < to {
count += 1;
if c.status == "down" {
down_count += 1;
}
}
// The span this check vouches for, clipped to the window.
let mut end = c.at.saturating_add(max_gap_seconds);
if i + 1 < checks.len() && checks[i + 1].at < end {
end = checks[i + 1].at;
}
let s = c.at.max(from);
let e = end.min(to);
if e > s {
match c.status.as_str() {
"up" => up += e - s,
"degraded" => degraded += e - s,
_ => down += e - s,
}
}
}
let known = up + degraded + down;
// Floored in i128: never 100.00% while a down second exists.
let uptime_basis_points = if known == 0 {
None
} else {
Some(((up + degraded) as i128 * 10000 / known as i128) as i64)
};
UptimeReport {
up_seconds: up,
degraded_seconds: degraded,
down_seconds: down,
unknown_seconds: to - from - known,
uptime_basis_points,
checks: count,
down_checks: down_count,
}
}
pub fn uptime_report_to_value(r: &UptimeReport) -> Value {
Value::obj(vec![
("upSeconds", Value::Int(r.up_seconds)),
("degradedSeconds", Value::Int(r.degraded_seconds)),
("downSeconds", Value::Int(r.down_seconds)),
("unknownSeconds", Value::Int(r.unknown_seconds)),
("uptimeBasisPoints", r.uptime_basis_points.map(Value::Int).unwrap_or(Value::Null)),
("checks", Value::Int(r.checks)),
("downChecks", Value::Int(r.down_checks)),
])
}
fn whole(v: &Value, msg: &str) -> i64 {
match v {
Value::Int(i) => *i,
other => panic!("{}{}", msg, other),
}
}
pub fn fune_vector(args: &[Value]) -> Value {
let checks = checks_from_value(&args[0]);
let from = match &args[1] { Value::Int(i) => *i, _ => panic!("from and to must be whole seconds") };
let to = match &args[2] { Value::Int(i) => *i, _ => panic!("from and to must be whole seconds") };
let gap = whole(&args[3], "maxGapSeconds must be a whole number of at least 1, received ");
uptime_report_to_value(&uptime(&checks, from, to, gap))
}