1use std::fmt::{self, Debug, Display};
6use std::time::Duration;
7
8#[derive(Copy, Clone, PartialEq, PartialOrd)]
10pub struct Throughput(f64);
11impl Throughput {
12 pub fn from_len_and_duration(len: u32, duration: Duration) -> Self {
13 let len = f64::from(len);
14 let secs = duration.as_secs_f64();
15
16 Self(len * 8f64 / secs)
17 }
18
19 pub fn as_f64(&self) -> f64 {
20 self.0
21 }
22}
23
24impl Debug for Throughput {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 write!(f, "{self}")
27 }
28}
29
30impl Display for Throughput {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 let Self(bits_per_second) = self;
33 let (value, suffix) = f64_to_value_and_suffix(*bits_per_second);
34 write!(f, "{value:.1} {suffix}bps")
35 }
36}
37
38fn f64_to_value_and_suffix(v: f64) -> (f64, &'static str) {
39 for (m, n) in [(1e9, "G"), (1e6, "M"), (1e3, "K")] {
40 if v >= m {
41 return (v / m, n);
42 }
43 }
44 (v, "")
45}
46
47#[derive(Copy, Clone)]
48pub struct BytesFormatter(pub u64);
49
50impl Display for BytesFormatter {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 let Self(v) = self;
53 let (value, suffix) = f64_to_value_and_suffix(*v as f64);
54 write!(f, "{value:.1} {suffix}B")
55 }
56}
57
58#[cfg(test)]
59mod test {
60 use super::*;
61 use test_case::test_case;
62
63 #[test]
64 fn value_and_suffix() {
65 assert_eq!(f64_to_value_and_suffix(1e12), (1e3, "G"));
66 assert_eq!(f64_to_value_and_suffix(2e9), (2.0, "G"));
67 assert_eq!(f64_to_value_and_suffix(2e6), (2.0, "M"));
68 assert_eq!(f64_to_value_and_suffix(2e3), (2.0, "K"));
69 assert_eq!(f64_to_value_and_suffix(2.0), (2.0, ""));
70 }
71
72 #[test_case(10, Duration::from_secs(1) => Throughput(80.0))]
73 #[test_case(1000, Duration::from_millis(8) => Throughput(1e6))]
74 #[test_case(1_000_000_000, Duration::from_secs(1) => Throughput(8e9))]
75 fn throughput_from_len_and_duration(len: u32, duration: Duration) -> Throughput {
76 Throughput::from_len_and_duration(len, duration)
77 }
78
79 #[test]
80 fn throughput_display() {
81 assert_eq!(Throughput(1.0).to_string(), "1.0 bps");
82 assert_eq!(Throughput(10e3).to_string(), "10.0 Kbps");
83 }
84
85 #[test]
86 fn bytes_display() {
87 assert_eq!(BytesFormatter(1).to_string(), "1.0 B");
88 assert_eq!(BytesFormatter(1000).to_string(), "1.0 KB");
89 }
90}