Functional Weave
Code in TypeScript

monitor.uptime-bars@1.0.0

impl/rust.rs

2,745 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::monitor_check_status::{checks_from_value, Check};  ← from monitor.check-status ^1.0.0 · built alongside by fune
use super::monitor_uptime::uptime;  ← from monitor.uptime ^1.0.0 · built alongside by fune
use super::time_unix_to_iso::unix_to_iso;  ← from time.unix-to-iso ^1.0.0 · built alongside by fune

const DAY: i64 = 86400;

/// The last `days` UTC days ending with today, oldest first, each measured by
/// monitor.uptime. Today's bar ends at `now`, so it never shows the rest of
/// today as unknown.
///
/// # Panics
/// Panics when days is outside 1..=366, and on anything monitor.uptime refuses.
pub fn uptime_bars(checks: &[Check], now: i64, days: i64, max_gap_seconds: i64) -> Vec<DayBar> {
    if !(1..=366).contains(&days) {
        panic!("days must be 1 to 366, received {}", days);
    }
    // Floored, not truncated: before 1970 today starts on the earlier midnight.
    let today_start = now - now.rem_euclid(DAY);
    let mut bars = Vec::new();
    for i in (0..days).rev() {
        let start = today_start - i * DAY;
        let end = if i == 0 { now } else { start + DAY };
        let r = uptime(checks, start, end, max_gap_seconds);
        let state = if r.uptime_basis_points.is_none() {
            "no-data"
        } else if r.down_seconds > 0 {
            "outage"
        } else if r.degraded_seconds > 0 {
            "degraded"
        } else {
            "operational"
        };
        bars.push(DayBar {
            date: unix_to_iso(start)[..10].to_string(),
            uptime_basis_points: r.uptime_basis_points,
            up_seconds: r.up_seconds,
            degraded_seconds: r.degraded_seconds,
            down_seconds: r.down_seconds,
            unknown_seconds: r.unknown_seconds,
            state: state.to_string(),
        });
    }
    bars
}

pub fn day_bar_to_value(b: &DayBar) -> Value {
    Value::obj(vec![
        ("date", Value::str(&b.date)),
        ("uptimeBasisPoints", b.uptime_basis_points.map(Value::Int).unwrap_or(Value::Null)),
        ("upSeconds", Value::Int(b.up_seconds)),
        ("degradedSeconds", Value::Int(b.degraded_seconds)),
        ("downSeconds", Value::Int(b.down_seconds)),
        ("unknownSeconds", Value::Int(b.unknown_seconds)),
        ("state", Value::str(&b.state)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let checks = checks_from_value(&args[0]);
    let now = match &args[1] {
        Value::Int(i) => *i,
        _ => panic!("now must be whole seconds"),
    };
    let days = match &args[2] {
        Value::Int(i) => *i,
        other => panic!("days must be 1 to 366, received {}", other),
    };
    let gap = match &args[3] {
        Value::Int(i) => *i,
        other => panic!("maxGapSeconds must be a whole number of at least 1, received {}", other),
    };
    Value::Arr(uptime_bars(&checks, now, days, gap).iter().map(day_bar_to_value).collect())
}