返回 CodeWhale
elapsed.rs
根目录 / crates / tui / src / elapsed.rs
1 //! Shared user-visible elapsed/duration formatter.
2 //!
3 //! Every TUI, workflow, Fleet, sub-agent, task/activity, toast, status, and
4 //! receipt surface renders durations through these helpers so the grammar
5 //! stays consistent everywhere: `0s`, `42s`, `1m 05s`, `12m 34s`.
6 //!
7 //! Rules:
8 //! - Whole seconds under a minute render bare (`0s`, `59s`).
9 //! - At one minute and above the seconds zero-pad (`60s` -> `1m 00s`,
10 //! `61s` -> `1m 01s`).
11 //! - Minutes keep growing past 60 (`3600s` -> `60m 00s`); no hours unit is
12 //! introduced, so a large value can never be misread at the wrong scale.
13 //! - Sub-second values render as milliseconds (`350ms`) so a fast receipt
14 //! never rounds down to a misleading `0s`.
15 //!
16 //! Display copy only: timeout/config input units, persisted values, and
17 //! machine-readable fields stay in their original units; this module exists
18 //! solely to render durations for humans.
19
20 /// Format a whole-seconds duration for a user-visible indicator.
21 #[must_use]
22 pub fn format_elapsed_secs(secs: u64) -> String {
23 if secs < 60 {
24 format!("{secs}s")
25 } else {
26 format!("{}m {:02}s", secs / 60, secs % 60)
27 }
28 }
29
30 /// Format a millisecond duration for a user-visible indicator.
31 ///
32 /// Sub-second values render as milliseconds (`350ms`); anything at or above
33 /// one second delegates to [`format_elapsed_secs`].
34 #[must_use]
35 pub fn format_elapsed_ms(ms: u64) -> String {
36 if ms == 0 {
37 "0s".to_string()
38 } else if ms < 1_000 {
39 format!("{ms}ms")
40 } else {
41 format_elapsed_secs(ms / 1_000)
42 }
43 }
44
45 #[cfg(test)]
46 mod tests {
47 use super::*;
48
49 #[test]
50 fn seconds_render_bare_under_a_minute() {
51 assert_eq!(format_elapsed_secs(0), "0s");
52 assert_eq!(format_elapsed_secs(1), "1s");
53 assert_eq!(format_elapsed_secs(42), "42s");
54 assert_eq!(format_elapsed_secs(59), "59s");
55 }
56
57 #[test]
58 fn minutes_zero_pad_seconds() {
59 assert_eq!(format_elapsed_secs(60), "1m 00s");
60 assert_eq!(format_elapsed_secs(61), "1m 01s");
61 assert_eq!(format_elapsed_secs(754), "12m 34s");
62 assert_eq!(format_elapsed_secs(3599), "59m 59s");
63 }
64
65 #[test]
66 fn minutes_keep_growing_past_one_hour() {
67 // Hours are intentionally not introduced; the value clamps at the
68 // minute/second representation.
69 assert_eq!(format_elapsed_secs(3600), "60m 00s");
70 assert_eq!(format_elapsed_secs(3661), "61m 01s");
71 }
72
73 #[test]
74 fn milliseconds_delegate_at_one_second() {
75 assert_eq!(format_elapsed_ms(0), "0s");
76 assert_eq!(format_elapsed_ms(350), "350ms");
77 assert_eq!(format_elapsed_ms(999), "999ms");
78 assert_eq!(format_elapsed_ms(1_000), "1s");
79 assert_eq!(format_elapsed_ms(59_999), "59s");
80 assert_eq!(format_elapsed_ms(60_000), "1m 00s");
81 assert_eq!(format_elapsed_ms(61_000), "1m 01s");
82 assert_eq!(format_elapsed_ms(3_599_000), "59m 59s");
83 assert_eq!(format_elapsed_ms(3_600_000), "60m 00s");
84 }
85 }
86
86 lines RUST