Functional Weave
Code in Python

monitor.heartbeat@1.0.0

impl/rust.rs

2,325 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

/// New, up, late or down, as healthchecks.io names them. Up through the due
/// second itself, late through the last second of the grace time, down
/// after. A ping stamped in the future (clock skew between the job and the
/// monitor) is simply up: its due time is later still.
///
/// # Panics
/// Panics when interval_seconds is below 1 or grace_seconds is negative.
pub fn heartbeat_status(last_ping_at: Option<i64>, now: i64, interval_seconds: i64, grace_seconds: i64) -> HeartbeatStatus {
    if interval_seconds < 1 {
        panic!("intervalSeconds must be at least 1, received {}", interval_seconds);
    }
    if grace_seconds < 0 {
        panic!("graceSeconds must not be negative, received {}", grace_seconds);
    }
    let last = match last_ping_at {
        None => return HeartbeatStatus { state: "new".to_string(), due_at: None, down_at: None, overdue_seconds: 0 },
        Some(l) => l,
    };
    let due_at = last + interval_seconds;
    let down_at = due_at + grace_seconds;
    if now <= due_at {
        return HeartbeatStatus { state: "up".to_string(), due_at: Some(due_at), down_at: Some(down_at), overdue_seconds: 0 };
    }
    let state = if now <= down_at { "late" } else { "down" };
    HeartbeatStatus { state: state.to_string(), due_at: Some(due_at), down_at: Some(down_at), overdue_seconds: now - due_at }
}

pub fn heartbeat_status_to_value(s: &HeartbeatStatus) -> Value {
    let opt = |o: Option<i64>| match o {
        Some(i) => Value::Int(i),
        None => Value::Null,
    };
    Value::obj(vec![
        ("state", Value::str(&s.state)),
        ("dueAt", opt(s.due_at)),
        ("downAt", opt(s.down_at)),
        ("overdueSeconds", Value::Int(s.overdue_seconds)),
    ])
}

fn whole(name: &str, v: &Value) -> i64 {
    match v {
        Value::Int(i) => *i,
        Value::Float(f) => panic!("{} must be whole seconds, received {}", name, f),
        _ => panic!("{} must be whole seconds", name),
    }
}

pub fn fune_vector(args: &[Value]) -> Value {
    let last = if args[0].is_null() { None } else { Some(whole("lastPingAt", &args[0])) };
    let s = heartbeat_status(
        last,
        whole("now", &args[1]),
        whole("intervalSeconds", &args[2]),
        whole("graceSeconds", &args[3]),
    );
    heartbeat_status_to_value(&s)
}