use super::funejson::Value; use super::monitor_check_status::{checks_from_value, Check}; use super::monitor_uptime::uptime; use super::time_unix_to_iso::unix_to_iso; 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 { 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()) }