net_bytes/
download_speed.rs1use std::time::Duration;
2
3use rust_decimal::Decimal;
4
5use crate::{FileSizeFormat, format_parts};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11pub struct DownloadSpeed {
12 bytes_per_second: Decimal,
13}
14
15impl DownloadSpeed {
16 #[inline]
26 pub fn from_raw(bytes_per_second: u64) -> Self {
27 Self {
28 bytes_per_second: Decimal::from(bytes_per_second),
29 }
30 }
31
32 pub fn new(bytes: u64, duration: Duration) -> Self {
44 let seconds = Decimal::from(duration.as_secs())
45 + Decimal::from(duration.subsec_nanos()) / Decimal::from(1_000_000_000);
46 let bytes_per_second = if seconds.is_zero() {
47 Decimal::ZERO
48 } else {
49 Decimal::from(bytes) / seconds
50 };
51
52 Self { bytes_per_second }
53 }
54
55 #[inline]
59 pub fn as_decimal(&self) -> Decimal {
60 self.bytes_per_second
61 }
62
63 #[inline]
67 pub fn as_u64(&self) -> u64 {
68 self.bytes_per_second.floor().try_into().unwrap_or(0)
69 }
70}
71
72impl FileSizeFormat for DownloadSpeed {
73 fn get_si_parts(&self) -> (String, &'static str) {
77 const UNITS: &[&str] = &[
78 "B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s", "EB/s", "ZB/s", "YB/s",
79 ];
80 format_parts(self.bytes_per_second, Decimal::from(1000), UNITS)
81 }
82
83 fn get_iec_parts(&self) -> (String, &'static str) {
87 const UNITS: &[&str] = &[
88 "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", "PiB/s", "EiB/s", "ZiB/s", "YiB/s",
89 ];
90 format_parts(self.bytes_per_second, Decimal::from(1024), UNITS)
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use std::time::Duration;
97
98 use rust_decimal::Decimal;
99
100 use super::DownloadSpeed;
101 use crate::{SizeStandard, FileSizeFormat};
102
103 fn format_test_si(bytes: u64) -> String {
107 DownloadSpeed::from_raw(bytes).to_formatted(SizeStandard::SI).to_string()
108 }
109
110 fn format_test_iec(bytes: u64) -> String {
114 DownloadSpeed::from_raw(bytes).to_formatted(SizeStandard::IEC).to_string()
115 }
116
117 #[test]
120 fn test_si_speed() {
121 assert_eq!(format_test_si(512), "512.0 B/s");
122 assert_eq!(format_test_si(1000), "1.00 KB/s");
123 assert_eq!(format_test_si(1024), "1.02 KB/s");
124 assert_eq!(format_test_si(9999), "10.00 KB/s");
125 assert_eq!(format_test_si(10_000), "10.0 KB/s");
126 assert_eq!(format_test_si(100_000), "100.0 KB/s");
127 }
128
129 #[test]
132 fn test_iec_speed() {
133 assert_eq!(format_test_iec(0), "0.00 B/s");
134 assert_eq!(format_test_iec(1024), "1.00 KiB/s");
135 assert_eq!(format_test_iec(1500), "1.46 KiB/s");
136
137 let bytes_near_10 = (9.999 * 1024.0) as u64;
138 assert_eq!(format_test_iec(bytes_near_10), "10.00 KiB/s");
139
140 assert_eq!(format_test_iec(10 * 1024), "10.0 KiB/s");
141 assert_eq!(format_test_iec(100 * 1024), "100.0 KiB/s");
142 }
143
144 #[test]
145 fn test_zero_speed() {
146 let zero_speed = DownloadSpeed::from_raw(0);
147 assert_eq!(zero_speed.as_u64(), 0);
148 assert_eq!(zero_speed.as_decimal(), Decimal::ZERO);
149
150 let formatted = zero_speed.to_formatted(SizeStandard::SI).to_string();
151 assert_eq!(formatted, "0.00 B/s");
152
153 let zero_duration = Duration::from_secs(0);
154 let zero_speed = DownloadSpeed::new(1000, zero_duration);
155 assert_eq!(zero_speed.as_u64(), 0);
156 }
157}