1#[derive(Clone, Copy, clap::ValueEnum)]
2pub enum Format {
3 #[value(name = "float")]
4 Float,
5 #[value(name = "hex")]
6 Hex,
7 #[value(name = "time")]
8 Time,
9 #[value(name = "bytes")]
10 Bytes,
11}
12
13impl Format {
14 pub fn format(&self, value: f64) -> String {
15 match self {
16 Format::Float => format!("{:.2}", value),
17 Format::Hex => format!("0x{:x}", value as u64),
18 Format::Time => format_duration(value),
19 Format::Bytes => format_bytes(value),
20 }
21 }
22}
23
24pub fn format_duration(ns: f64) -> String {
25 if ns < 1e3 {
26 format!("{:.2}ns", ns)
27 } else if ns < 1e6 {
28 format!("{:.2}µs", ns / 1e3)
29 } else if ns < 1e9 {
30 format!("{:.2}ms", ns / 1e6)
31 } else if ns < 60e9 {
32 format!("{:.2}s", ns / 1e9)
33 } else if ns < 3600e9 {
34 let mins = (ns / 60e9).floor();
35 let secs = (ns - mins * 60e9) / 1e9;
36 format!("{}m{:.2}s", mins as i64, secs)
37 } else {
38 let hours = (ns / 3600e9).floor();
39 let mins = ((ns - hours * 3600e9) / 60e9).floor();
40 let secs = (ns - hours * 3600e9 - mins * 60e9) / 1e9;
41 format!("{}h{}m{:.2}s", hours as i64, mins as i64, secs)
42 }
43}
44
45pub fn format_bytes(bytes: f64) -> String {
46 let units = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
47 let mut value = bytes;
48 let mut unit_idx = 0;
49
50 while value >= 1024.0 && unit_idx < units.len() - 1 {
51 value /= 1024.0;
52 unit_idx += 1;
53 }
54
55 if unit_idx == 0 {
56 format!("{:.0}{}", value, units[unit_idx])
57 } else {
58 format!("{:.2}{}", value, units[unit_idx])
59 }
60}
61
62pub fn get_display_scale(max_value: f64, format: Format) -> (f64, &'static str) {
65 match format {
66 Format::Time => {
67 if max_value < 1e3 {
69 (1.0, "ns")
70 } else if max_value < 1e6 {
71 (1e3, "µs")
72 } else if max_value < 1e9 {
73 (1e6, "ms")
74 } else {
75 (1e9, "s")
76 }
77 }
78 Format::Bytes => {
79 if max_value < 1024.0 {
81 (1.0, "B")
82 } else if max_value < 1024.0_f64.powi(2) {
83 (1024.0, "KiB")
84 } else if max_value < 1024.0_f64.powi(3) {
85 (1024.0_f64.powi(2), "MiB")
86 } else if max_value < 1024.0_f64.powi(4) {
87 (1024.0_f64.powi(3), "GiB")
88 } else if max_value < 1024.0_f64.powi(5) {
89 (1024.0_f64.powi(4), "TiB")
90 } else {
91 (1024.0_f64.powi(5), "PiB")
92 }
93 }
94 Format::Float => (1.0, ""),
95 Format::Hex => (1.0, ""),
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_format_duration_nanoseconds() {
105 assert_eq!(format_duration(1.0), "1.00ns");
106 assert_eq!(format_duration(500.0), "500.00ns");
107 assert_eq!(format_duration(999.0), "999.00ns");
108 }
109
110 #[test]
111 fn test_format_duration_microseconds() {
112 assert_eq!(format_duration(1e3), "1.00µs");
113 assert_eq!(format_duration(5e3), "5.00µs");
114 assert_eq!(format_duration(500e3), "500.00µs");
115 }
116
117 #[test]
118 fn test_format_duration_milliseconds() {
119 assert_eq!(format_duration(1e6), "1.00ms");
120 assert_eq!(format_duration(5e6), "5.00ms");
121 assert_eq!(format_duration(500e6), "500.00ms");
122 }
123
124 #[test]
125 fn test_format_duration_seconds() {
126 assert_eq!(format_duration(1e9), "1.00s");
127 assert_eq!(format_duration(5e9), "5.00s");
128 assert_eq!(format_duration(30e9), "30.00s");
129 }
130
131 #[test]
132 fn test_format_duration_minutes() {
133 assert_eq!(format_duration(60e9), "1m0.00s");
134 assert_eq!(format_duration(90e9), "1m30.00s");
135 assert_eq!(format_duration(150e9), "2m30.00s");
136 }
137
138 #[test]
139 fn test_format_duration_hours() {
140 assert_eq!(format_duration(3600e9), "1h0m0.00s");
141 assert_eq!(format_duration(3661e9), "1h1m1.00s");
142 assert_eq!(format_duration(7384e9), "2h3m4.00s");
143 }
144
145 #[test]
146 fn test_format_bytes_bytes() {
147 assert_eq!(format_bytes(0.0), "0B");
148 assert_eq!(format_bytes(100.0), "100B");
149 assert_eq!(format_bytes(1023.0), "1023B");
150 }
151
152 #[test]
153 fn test_format_bytes_kibibytes() {
154 assert_eq!(format_bytes(1024.0), "1.00KiB");
155 assert_eq!(format_bytes(2048.0), "2.00KiB");
156 assert_eq!(format_bytes(1536.0), "1.50KiB");
157 }
158
159 #[test]
160 fn test_format_bytes_mebibytes() {
161 assert_eq!(format_bytes(1024.0 * 1024.0), "1.00MiB");
162 assert_eq!(format_bytes(2.5 * 1024.0 * 1024.0), "2.50MiB");
163 }
164
165 #[test]
166 fn test_format_bytes_gibibytes() {
167 assert_eq!(format_bytes(1024.0_f64.powi(3)), "1.00GiB");
168 assert_eq!(format_bytes(5.5 * 1024.0_f64.powi(3)), "5.50GiB");
169 }
170
171 #[test]
172 fn test_format_bytes_tebibytes() {
173 assert_eq!(format_bytes(1024.0_f64.powi(4)), "1.00TiB");
174 assert_eq!(format_bytes(2.75 * 1024.0_f64.powi(4)), "2.75TiB");
175 }
176
177 #[test]
178 fn test_format_bytes_pebibytes() {
179 assert_eq!(format_bytes(1024.0_f64.powi(5)), "1.00PiB");
180 assert_eq!(format_bytes(2.75 * 1024.0_f64.powi(5)), "2.75PiB");
181 }
182
183 #[test]
184 fn test_format_float() {
185 assert_eq!(Format::Float.format(42.567), "42.57");
186 assert_eq!(Format::Float.format(0.123), "0.12");
187 assert_eq!(Format::Float.format(1000.0), "1000.00");
188 }
189
190 #[test]
191 fn test_format_hex() {
192 assert_eq!(Format::Hex.format(255.0), "0xff");
193 assert_eq!(Format::Hex.format(16.0), "0x10");
194 assert_eq!(Format::Hex.format(0.0), "0x0");
195 }
196
197 #[test]
198 fn test_format_time() {
199 assert_eq!(Format::Time.format(1e6), "1.00ms");
200 assert_eq!(Format::Time.format(60e9), "1m0.00s");
201 }
202
203 #[test]
204 fn test_format_bytes_format() {
205 assert_eq!(Format::Bytes.format(1024.0), "1.00KiB");
206 assert_eq!(Format::Bytes.format(1024.0_f64.powi(2)), "1.00MiB");
207 }
208
209 #[test]
210 fn test_get_display_scale_time_nanoseconds() {
211 let (scale, unit) = get_display_scale(500.0, Format::Time);
212 assert_eq!(scale, 1.0);
213 assert_eq!(unit, "ns");
214 }
215
216 #[test]
217 fn test_get_display_scale_time_microseconds() {
218 let (scale, unit) = get_display_scale(5e3, Format::Time);
219 assert_eq!(scale, 1e3);
220 assert_eq!(unit, "µs");
221 }
222
223 #[test]
224 fn test_get_display_scale_time_milliseconds() {
225 let (scale, unit) = get_display_scale(5e6, Format::Time);
226 assert_eq!(scale, 1e6);
227 assert_eq!(unit, "ms");
228 }
229
230 #[test]
231 fn test_get_display_scale_time_seconds() {
232 let (scale, unit) = get_display_scale(5e9, Format::Time);
233 assert_eq!(scale, 1e9);
234 assert_eq!(unit, "s");
235 }
236
237 #[test]
238 fn test_get_display_scale_bytes_b() {
239 let (scale, unit) = get_display_scale(512.0, Format::Bytes);
240 assert_eq!(scale, 1.0);
241 assert_eq!(unit, "B");
242 }
243
244 #[test]
245 fn test_get_display_scale_bytes_kib() {
246 let (scale, unit) = get_display_scale(2048.0, Format::Bytes);
247 assert_eq!(scale, 1024.0);
248 assert_eq!(unit, "KiB");
249 }
250
251 #[test]
252 fn test_get_display_scale_bytes_mib() {
253 let (scale, unit) = get_display_scale(5.0 * 1024.0_f64.powi(2), Format::Bytes);
254 assert_eq!(scale, 1024.0_f64.powi(2));
255 assert_eq!(unit, "MiB");
256 }
257
258 #[test]
259 fn test_get_display_scale_float() {
260 let (scale, unit) = get_display_scale(1000.0, Format::Float);
261 assert_eq!(scale, 1.0);
262 assert_eq!(unit, "");
263 }
264
265 #[test]
266 fn test_get_display_scale_hex() {
267 let (scale, unit) = get_display_scale(255.0, Format::Hex);
268 assert_eq!(scale, 1.0);
269 assert_eq!(unit, "");
270 }
271}