pub fn compact(secs: u64) -> String {
if secs < 60 {
format!("{secs}s")
} else if secs < 3_600 {
format!("{}m", secs / 60)
} else if secs < 86_400 {
format!("{}h", secs / 3_600)
} else {
format!("{}d", secs / 86_400)
}
}
pub fn precise(secs: u64) -> String {
if secs < 60 {
format!("{secs}s")
} else if secs < 3_600 {
format!("{}m{}s", secs / 60, secs % 60)
} else {
format!("{}h{}m", secs / 3_600, (secs % 3_600) / 60)
}
}
pub fn between(from: i64, to: i64) -> u64 {
to.saturating_sub(from).max(0) as u64
}
pub fn now_secs() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0)
}
pub const AGE_SECS_KEY: &str = "age_secs";
pub const WORKING_SECS_KEY: &str = "working_secs";
pub fn annotate_spans(value: &mut serde_json::Value, age_secs: u64, working_secs: u64) {
if let serde_json::Value::Object(map) = value {
map.insert(AGE_SECS_KEY.to_string(), age_secs.into());
map.insert(WORKING_SECS_KEY.to_string(), working_secs.into());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compact_uses_one_unit_and_names_each_boundary() {
assert_eq!(compact(0), "0s");
assert_eq!(compact(59), "59s");
assert_eq!(compact(60), "1m");
assert_eq!(compact(3_599), "59m");
assert_eq!(compact(3_600), "1h");
assert_eq!(compact(86_399), "23h");
assert_eq!(compact(86_400), "1d");
assert_eq!(compact(172_800), "2d");
}
#[test]
fn precise_uses_two_units_and_names_each_boundary() {
assert_eq!(precise(0), "0s");
assert_eq!(precise(45), "45s");
assert_eq!(precise(59), "59s");
assert_eq!(precise(60), "1m0s");
assert_eq!(precise(125), "2m5s");
assert_eq!(precise(3_599), "59m59s");
assert_eq!(precise(3_600), "1h0m");
assert_eq!(precise(4_800), "1h20m");
}
#[test]
fn annotate_spans_adds_both_keys_and_keeps_the_raw_stamps() {
let mut v = serde_json::json!({ "run_id": "r", "started_at": 100 });
annotate_spans(&mut v, 3_840, 720);
assert_eq!(v[AGE_SECS_KEY], 3_840);
assert_eq!(v[WORKING_SECS_KEY], 720);
assert_eq!(v["started_at"], 100, "the raw stamps are not replaced");
}
#[test]
fn annotate_spans_leaves_a_non_object_alone() {
let mut v = serde_json::Value::Null;
annotate_spans(&mut v, 1, 2);
assert_eq!(v, serde_json::Value::Null);
}
#[test]
fn between_clamps_a_clock_that_moved_backwards() {
assert_eq!(between(100, 145), 45);
assert_eq!(between(100, 100), 0);
assert_eq!(between(200, 100), 0);
}
#[test]
fn between_survives_a_timestamp_at_the_edge_of_the_range() {
assert_eq!(between(i64::MIN, 0), i64::MAX as u64);
assert_eq!(between(0, i64::MIN), 0);
}
#[test]
fn now_secs_is_a_plausible_unix_time() {
let now = now_secs();
assert!((1_704_067_200..4_102_444_800).contains(&now), "{now}");
}
}