gglib_core/download/
format.rs1pub const UNKNOWN: &str = "—";
24
25const KB: f64 = 1_000.0;
26const MB: f64 = 1_000_000.0;
27const GB: f64 = 1_000_000_000.0;
28
29#[must_use]
34pub fn format_rate(bps: Option<f64>) -> String {
35 let Some(bps) = bps.filter(|v| v.is_finite() && *v >= 0.0) else {
36 return UNKNOWN.to_string();
37 };
38
39 if bps >= GB {
40 format!("{:.2} GB/s", bps / GB)
41 } else if bps >= MB {
42 format!("{:.1} MB/s", bps / MB)
43 } else if bps >= KB {
44 format!("{:.0} kB/s", bps / KB)
45 } else {
46 format!("{bps:.0} B/s")
47 }
48}
49
50#[must_use]
56pub fn format_duration(seconds: Option<f64>) -> String {
57 let Some(seconds) = seconds.filter(|v| v.is_finite() && *v >= 0.0) else {
58 return UNKNOWN.to_string();
59 };
60
61 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
64 let total = seconds.ceil().min(359_999.0) as u64;
65
66 let (hours, minutes, secs) = (total / 3600, (total % 3600) / 60, total % 60);
67
68 if hours > 0 {
69 format!("{hours}h {minutes:02}m")
70 } else if minutes > 0 {
71 format!("{minutes}m {secs:02}s")
72 } else {
73 format!("{}s", total.max(1))
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn unknown_rate_renders_a_placeholder() {
83 assert_eq!(format_rate(None), UNKNOWN);
84 assert_eq!(format_rate(Some(f64::NAN)), UNKNOWN);
85 assert_eq!(format_rate(Some(f64::INFINITY)), UNKNOWN);
86 assert_eq!(format_rate(Some(-1.0)), UNKNOWN);
87 }
88
89 #[test]
90 fn rates_use_decimal_units() {
91 assert_eq!(format_rate(Some(0.0)), "0 B/s");
92 assert_eq!(format_rate(Some(999.0)), "999 B/s");
93 assert_eq!(format_rate(Some(1_000.0)), "1 kB/s");
94 assert_eq!(format_rate(Some(1_500_000.0)), "1.5 MB/s");
95 assert_eq!(format_rate(Some(118_400_000.0)), "118.4 MB/s");
96 assert_eq!(format_rate(Some(2_500_000_000.0)), "2.50 GB/s");
97 }
98
99 #[test]
100 fn a_megabyte_per_second_is_a_million_bytes() {
101 assert_eq!(format_rate(Some(1_048_576.0)), "1.0 MB/s");
104 assert_eq!(format_rate(Some(1_000_000.0)), "1.0 MB/s");
105 }
106
107 #[test]
108 fn unknown_duration_renders_a_placeholder() {
109 assert_eq!(format_duration(None), UNKNOWN);
110 assert_eq!(format_duration(Some(f64::NAN)), UNKNOWN);
111 assert_eq!(format_duration(Some(-5.0)), UNKNOWN);
112 }
113
114 #[test]
115 fn durations_scale_by_magnitude() {
116 assert_eq!(format_duration(Some(0.0)), "1s");
117 assert_eq!(format_duration(Some(45.0)), "45s");
118 assert_eq!(format_duration(Some(59.4)), "1m 00s");
119 assert_eq!(format_duration(Some(200.0)), "3m 20s");
120 assert_eq!(format_duration(Some(3_600.0)), "1h 00m");
121 assert_eq!(format_duration(Some(3_845.0)), "1h 04m");
122 }
123
124 #[test]
125 fn absurd_durations_saturate_instead_of_wrapping() {
126 assert_eq!(format_duration(Some(1e18)), "99h 59m");
127 }
128}