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