1use std::fmt::Write as _;
7
8use arrayvec::ArrayString;
9
10pub fn bytes_human(bytes: u64) -> ArrayString<16> {
12 const UNITS: [&str; 6] = ["B", "K", "M", "G", "T", "P"];
13 let mut out = ArrayString::new();
14 if bytes < 1024 {
15 let _ = write!(out, "{bytes}B");
16 return out;
17 }
18 let mut value = bytes as f64;
19 let mut unit = 0;
20 while value >= 1024.0 && unit + 1 < UNITS.len() {
21 value /= 1024.0;
22 unit += 1;
23 }
24 let _ = if value < 10.0 {
26 write!(out, "{value:.1}{}", UNITS[unit])
27 } else {
28 write!(out, "{value:.0}{}", UNITS[unit])
29 };
30 out
31}
32
33pub fn duration_verbose(secs: u64) -> ArrayString<24> {
50 let mut out = ArrayString::new();
51 let d = secs / 86_400;
52 let h = (secs % 86_400) / 3_600;
53 let m = (secs % 3_600) / 60;
54 let s = secs % 60;
55
56 let _ = if d > 0 {
57 if h > 0 {
58 write!(out, "{d}d {h}h")
59 } else {
60 write!(out, "{d}d")
61 }
62 } else if h > 0 {
63 if m > 0 {
64 write!(out, "{h}h {m}m")
65 } else {
66 write!(out, "{h}h")
67 }
68 } else if m > 0 {
69 if s > 0 {
70 write!(out, "{m}m {s}s")
71 } else {
72 write!(out, "{m}m")
73 }
74 } else {
75 write!(out, "{s}s")
76 };
77 out
78}
79
80pub fn duration_human(secs: u64) -> ArrayString<16> {
85 let mut out = ArrayString::new();
86 let d = secs / 86_400;
87 let h = (secs % 86_400) / 3_600;
88 let m = (secs % 3_600) / 60;
89 let s = secs % 60;
90 let _ = if d > 0 {
91 write!(out, "{d}-{h:02}:{m:02}:{s:02}")
92 } else if h > 0 {
93 write!(out, "{h}:{m:02}:{s:02}")
94 } else {
95 write!(out, "{m:02}:{s:02}")
96 };
97 out
98}
99
100pub fn cpu_time_human(ticks: u64, clk_tck: u64) -> ArrayString<16> {
102 let mut out = ArrayString::new();
103 if clk_tck == 0 {
104 out.push('-');
105 return out;
106 }
107 let centis = ticks * 100 / clk_tck;
108 let m = centis / 6_000;
109 let s = (centis % 6_000) / 100;
110 let c = centis % 100;
111 let _ = write!(out, "{m:02}:{s:02}.{c:02}");
112 out
113}
114
115pub fn percent_human(p: f32) -> ArrayString<8> {
117 let mut out = ArrayString::new();
118 if p.is_nan() {
119 out.push('-');
120 return out;
121 }
122 let _ = write!(out, "{p:.1}");
123 out
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn bytes_scaling() {
132 assert_eq!(&*bytes_human(0), "0B");
133 assert_eq!(&*bytes_human(512), "512B");
134 assert_eq!(&*bytes_human(1024), "1.0K");
135 assert_eq!(&*bytes_human(1536), "1.5K");
136 assert_eq!(&*bytes_human(1024 * 1024 * 200), "200M");
137 }
138
139 #[test]
140 fn durations() {
141 assert_eq!(&*duration_human(0), "00:00");
142 assert_eq!(&*duration_human(75), "01:15");
143 assert_eq!(&*duration_human(3_725), "1:02:05");
144 assert_eq!(&*duration_human(90_061), "1-01:01:01");
145 }
146
147 #[test]
148 fn verbose_durations_have_units() {
149 assert_eq!(&*duration_verbose(0), "0s");
150 assert_eq!(&*duration_verbose(45), "45s");
151 assert_eq!(&*duration_verbose(60), "1m");
152 assert_eq!(&*duration_verbose(90), "1m 30s");
153 assert_eq!(&*duration_verbose(3_600), "1h");
154 assert_eq!(&*duration_verbose(3_725), "1h 2m");
155 assert_eq!(&*duration_verbose(86_400), "1d");
156 assert_eq!(&*duration_verbose(90_061), "1d 1h");
157 assert_eq!(
158 &*duration_verbose(4 * 86_400 + 23 * 3_600 + 13 * 60),
159 "4d 23h"
160 );
161 }
162
163 #[test]
164 fn verbose_durations_fit_the_buffer() {
165 let ten_years = 10 * 365 * 86_400u64;
167 let text = duration_verbose(ten_years);
168 assert!(!text.is_empty());
169 assert!(text.len() < 24, "got {text:?}");
170 }
171
172 #[test]
173 fn cpu_time() {
174 assert_eq!(&*cpu_time_human(100, 100), "00:01.00");
176 assert_eq!(&*cpu_time_human(0, 0), "-");
177 }
178}