net_bytes/
download_acceleration.rs

1use std::fmt;
2use std::num::NonZeroU64;
3use std::time::Duration;
4
5use rust_decimal::Decimal;
6
7use crate::{SizeStandard, format_parts};
8
9/// Download acceleration in bytes per second squared
10/// 
11/// 下载加速度(单位:字节每秒平方)
12#[derive(Debug, Clone, Copy)]
13pub struct DownloadAcceleration {
14    bytes_per_second_sq: Decimal,
15    standard: SizeStandard,
16}
17
18impl PartialEq for DownloadAcceleration {
19    #[inline]
20    fn eq(&self, other: &Self) -> bool {
21        self.bytes_per_second_sq == other.bytes_per_second_sq
22    }
23}
24
25impl PartialOrd for DownloadAcceleration {
26    #[inline]
27    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
28        self.bytes_per_second_sq
29            .partial_cmp(&other.bytes_per_second_sq)
30    }
31}
32
33impl DownloadAcceleration {
34    /// Create a new download acceleration from raw bytes per second squared
35    /// 
36    /// # Parameters
37    /// - `bytes_per_second_sq`: Non-zero acceleration in bytes per second squared
38    /// - `standard`: Standard to use (SI or IEC)
39    ///
40    /// # Note
41    /// For negative acceleration, use `as u64` conversion of `i64` to ensure proper sign handling
42    /// 
43    /// 从原始字节/秒²创建一个新的下载加速度实例
44    /// 
45    /// # 参数
46    /// - `bytes_per_second_sq`: 非零的字节/秒²加速度值
47    /// - `standard`: 使用的标准(SI 或 IEC)
48    ///
49    /// # 注意
50    /// 对于负加速度,使用 `i64` 的 `as u64` 转换,确保正确处理符号位
51    #[inline]
52    pub fn from_raw(bytes_per_second_sq: i64, standard: SizeStandard) -> Self {
53        Self {
54            bytes_per_second_sq: Decimal::from(bytes_per_second_sq),
55            standard,
56        }
57    }
58
59    /// Create a new download acceleration from speed change and time interval
60    /// 
61    /// # Parameters
62    /// - `initial_speed`: Initial speed in non-zero bytes per second
63    /// - `final_speed`: Final speed in non-zero bytes per second
64    /// - `duration`: Time interval for the speed change
65    /// - `standard`: Standard to use (SI or IEC)
66    ///
67    /// # Panics
68    /// Panics if `initial_speed` or `final_speed` is zero
69    ///
70    /// 从下载速度变化量和时间间隔创建一个新的下载加速度实例
71    ///
72    /// # 参数
73    /// - `initial_speed`: 初始速度(非零字节/秒)
74    /// - `final_speed`: 最终速度(非零字节/秒)
75    /// - `duration`: 速度变化所用的时间
76    /// - `standard`: 使用的标准(SI 或 IEC)
77    ///
78    /// # Panics
79    /// 如果 `initial_speed` 或 `final_speed` 为零,此函数会 panic。
80    pub fn new(
81        initial_speed: NonZeroU64,
82        final_speed: NonZeroU64,
83        duration: Duration,
84        standard: SizeStandard,
85    ) -> Self {
86        let seconds = Decimal::from(duration.as_secs())
87            + Decimal::from(duration.subsec_nanos()) / Decimal::from(1_000_000_000);
88        let speed_diff = Decimal::from(final_speed.get()) - Decimal::from(initial_speed.get());
89        let bytes_per_second_sq = if seconds.is_zero() {
90            Decimal::ZERO
91        } else {
92            speed_diff / seconds
93        };
94
95        Self {
96            bytes_per_second_sq,
97            standard,
98        }
99    }
100
101    /// Get the acceleration in bytes per second squared as a `Decimal`
102    /// 
103    /// 以 `Decimal` 的形式获取字节每秒平方
104    #[inline]
105    pub fn as_decimal(&self) -> Decimal {
106        self.bytes_per_second_sq
107    }
108
109    /// Get the acceleration in bytes per second squared as `i64` (floored)
110    /// 
111    /// 以 `i64` 的形式获取字节每秒平方(向下取整)
112    #[inline]
113    pub fn as_i64(&self) -> i64 {
114        self.bytes_per_second_sq.floor().try_into().unwrap_or(0)
115    }
116
117    /// Returns the formatted value and unit in SI (base-1000) standard
118    /// 
119    /// 返回 SI (base-1000) 标准的 (formatted_value, unit)
120    pub fn get_si_parts(&self) -> (String, &'static str) {
121        const UNITS: &[&str] = &[
122            "B/s²", "KB/s²", "MB/s²", "GB/s²", "TB/s²", "PB/s²", "EB/s²", "ZB/s²", "YB/s²",
123        ];
124        self.format_parts(Decimal::from(1000), UNITS)
125    }
126
127    /// Returns the formatted value and unit in IEC (base-1024) standard
128    /// 
129    /// 返回 IEC (base-1024) 标准的 (formatted_value, unit)
130    pub fn get_iec_parts(&self) -> (String, &'static str) {
131        const UNITS: &[&str] = &[
132            "B/s²", "KiB/s²", "MiB/s²", "GiB/s²", "TiB/s²", "PiB/s²", "EiB/s²", "ZiB/s²", "YiB/s²",
133        ];
134        self.format_parts(Decimal::from(1024), UNITS)
135    }
136
137    fn format_parts(
138        &self,
139        base: Decimal,
140        units: &'static [&'static str],
141    ) -> (String, &'static str) {
142        let mut value = Decimal::from(self.bytes_per_second_sq);
143        let is_negative = value.is_sign_negative();
144
145        if is_negative {
146            value.set_sign_positive(true);
147        }
148
149        let (formatted_value, unit) = format_parts(value, base, units);
150        let formatted_value = if is_negative {
151            format!("-{}", formatted_value)
152        } else {
153            formatted_value
154        };
155
156        (formatted_value, unit)
157    }
158}
159
160impl fmt::Display for DownloadAcceleration {
161    #[inline]
162    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163        let (value, unit) = match self.standard {
164            SizeStandard::SI => self.get_si_parts(),
165            SizeStandard::IEC => self.get_iec_parts(),
166        };
167        write!(f, "{} {}", value, unit)
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::DownloadAcceleration;
174    use crate::SizeStandard;
175    use std::num::NonZeroU64;
176    use std::time::Duration;
177
178    #[test]
179    fn test_si_acceleration() {
180        let acc = DownloadAcceleration::from_raw(512, SizeStandard::SI);
181        assert_eq!(acc.to_string(), "512.0 B/s²");
182
183        let one_second = Duration::from_secs(1);
184        let acc = DownloadAcceleration::new(
185            NonZeroU64::new(100).unwrap(),
186            NonZeroU64::new(1100).unwrap(),
187            one_second,
188            SizeStandard::SI,
189        );
190        assert_eq!(acc.to_string(), "1.00 KB/s²");
191
192        let acc = DownloadAcceleration::new(
193            NonZeroU64::new(1).unwrap(),
194            NonZeroU64::new(10000).unwrap(),
195            one_second,
196            SizeStandard::SI,
197        );
198        assert_eq!(acc.to_string(), "10.00 KB/s²");
199
200        let acc = DownloadAcceleration::new(
201            NonZeroU64::new(1000).unwrap(),
202            NonZeroU64::new(11000).unwrap(),
203            one_second,
204            SizeStandard::SI,
205        );
206        assert_eq!(acc.to_string(), "10.0 KB/s²");
207
208        // Test negative acceleration from raw value
209        let acc_neg = DownloadAcceleration::from_raw(-1500, SizeStandard::SI);
210        assert_eq!(acc_neg.to_string(), "-1.50 KB/s²");
211    }
212
213    #[test]
214    fn test_iec_acceleration() {
215        let one_second = Duration::from_secs(1);
216        let acc = DownloadAcceleration::new(
217            NonZeroU64::new(100).unwrap(),
218            NonZeroU64::new(1124).unwrap(),
219            one_second,
220            SizeStandard::IEC,
221        );
222        assert_eq!(acc.to_string(), "1.00 KiB/s²");
223
224        // Test negative acceleration from raw value in IEC standard
225        let acc_neg = DownloadAcceleration::from_raw(-1500, SizeStandard::IEC);
226        assert_eq!(acc_neg.to_string(), "-1.46 KiB/s²");
227
228        let acc = DownloadAcceleration::new(
229            NonZeroU64::new(1024).unwrap(),
230            NonZeroU64::new(11 * 1024).unwrap(),
231            one_second,
232            SizeStandard::IEC,
233        );
234        assert_eq!(acc.to_string(), "10.0 KiB/s²");
235
236        let acc = DownloadAcceleration::new(
237            NonZeroU64::new(10 * 1024).unwrap(),
238            NonZeroU64::new(110 * 1024).unwrap(),
239            one_second,
240            SizeStandard::IEC,
241        );
242        assert_eq!(acc.to_string(), "100.0 KiB/s²");
243    }
244}