feat(cli): render uptime in human units
The UPTIME column printed raw seconds, so a server up for three hours read as 10549s. Format the two largest non-zero units instead, dropping the smaller one when it is zero: 49s, 12m 4s, 2h 55m, 6d 3h. Days is the largest unit and the output never exceeds eight characters, so the ten-wide column still holds and RESTARTS stays put. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9BWMXKKNpid7XM6oFbyYq
This commit is contained in:
@@ -7,10 +7,7 @@ pub fn list_table(rows: &[ServerSummary]) -> String {
|
||||
|
||||
for r in rows {
|
||||
let pid = r.pid.map(|p| p.to_string()).unwrap_or_else(|| "-".into());
|
||||
let up = r
|
||||
.uptime_secs
|
||||
.map(|s| format!("{}s", s))
|
||||
.unwrap_or_else(|| "-".into());
|
||||
let up = r.uptime_secs.map(uptime).unwrap_or_else(|| "-".into());
|
||||
|
||||
out.push_str(&format!(
|
||||
"{:<20}{:<12}{:<8}{:<8}{:<10}{}\n",
|
||||
@@ -26,6 +23,28 @@ pub fn list_table(rows: &[ServerSummary]) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
fn uptime(secs: u64) -> String {
|
||||
const MINUTE: u64 = 60;
|
||||
const HOUR: u64 = 60 * MINUTE;
|
||||
const DAY: u64 = 24 * HOUR;
|
||||
|
||||
let (major, minor, major_unit, minor_unit) = if secs >= DAY {
|
||||
(secs / DAY, secs % DAY / HOUR, "d", "h")
|
||||
} else if secs >= HOUR {
|
||||
(secs / HOUR, secs % HOUR / MINUTE, "h", "m")
|
||||
} else if secs >= MINUTE {
|
||||
(secs / MINUTE, secs % MINUTE, "m", "s")
|
||||
} else {
|
||||
return format!("{secs}s");
|
||||
};
|
||||
|
||||
if minor == 0 {
|
||||
return format!("{major}{major_unit}");
|
||||
}
|
||||
|
||||
format!("{major}{major_unit} {minor}{minor_unit}")
|
||||
}
|
||||
|
||||
pub(crate) fn wait_line(info: &WaitInfo) -> String {
|
||||
format!(
|
||||
" wait-for: {}\n {}s elapsed, timeout {}s\n",
|
||||
@@ -55,6 +74,62 @@ mod tests {
|
||||
assert!(out.contains("waiting"), "got: {out}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uptime_under_a_minute_is_bare_seconds() {
|
||||
assert_eq!(uptime(0), "0s");
|
||||
assert_eq!(uptime(49), "49s");
|
||||
assert_eq!(uptime(59), "59s");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uptime_under_an_hour_is_minutes_and_seconds() {
|
||||
assert_eq!(uptime(60), "1m");
|
||||
assert_eq!(uptime(724), "12m 4s");
|
||||
assert_eq!(uptime(3_599), "59m 59s");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uptime_under_a_day_is_hours_and_minutes() {
|
||||
assert_eq!(uptime(3_600), "1h");
|
||||
assert_eq!(uptime(10_549), "2h 55m");
|
||||
assert_eq!(uptime(86_399), "23h 59m");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uptime_of_a_day_or_more_is_days_and_hours() {
|
||||
assert_eq!(uptime(86_400), "1d");
|
||||
assert_eq!(uptime(529_200), "6d 3h");
|
||||
assert_eq!(uptime(2_707_200), "31d 8h");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uptime_drops_a_trailing_zero_unit() {
|
||||
assert_eq!(uptime(120), "2m");
|
||||
assert_eq!(uptime(7_200), "2h");
|
||||
assert_eq!(uptime(518_400), "6d");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_renders_uptime_in_human_units() {
|
||||
let rows = vec![ServerSummary {
|
||||
name: "vestige".to_string(),
|
||||
state: ServerState::Running,
|
||||
pid: Some(4821),
|
||||
port: 3928,
|
||||
uptime_secs: Some(10_549),
|
||||
restart_count: 0,
|
||||
last_exit: None,
|
||||
}];
|
||||
|
||||
let out = list_table(&rows);
|
||||
|
||||
assert!(out.contains("2h 55m"), "got: {out}");
|
||||
assert!(
|
||||
!out.contains("10549"),
|
||||
"raw seconds must not leak; got: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_renders_the_wait_condition_and_elapsed() {
|
||||
let info = WaitInfo {
|
||||
|
||||
Reference in New Issue
Block a user