Skip to main content

flo_pv/
stats.rs

1//! The stats module contains the stats loop
2//!
3//! # Header
4//! ...
5mod timer;
6
7use crossbeam::channel::Receiver;
8use crossterm::{
9    cursor, execute,
10    style::{self, Color, PrintStyledContent},
11    terminal::{Clear, ClearType},
12};
13use std::io::{self, Result, Stderr, Write};
14use std::time::Instant;
15use timer::Timer;
16
17pub fn stats_loop(silent: bool, stats_rx: Receiver<usize>) -> Result<()> {
18    let mut total_bytes = 0;
19    let start = Instant::now();
20    let mut timer = Timer::new();
21    let mut stderr = io::stderr();
22    loop {
23        let num_bytes = stats_rx.recv().unwrap();
24        timer.update();
25        let rate_per_second = num_bytes as f64 / timer.delta.as_secs_f64();
26        total_bytes += num_bytes;
27        if !silent && timer.ready {
28            timer.ready = false;
29            output_progress(
30                &mut stderr,
31                total_bytes,
32                start.elapsed().as_secs().as_time(),
33                rate_per_second,
34            );
35        }
36        if num_bytes == 0 {
37            break;
38        }
39    }
40
41    if !silent {
42        eprintln!();
43    }
44
45    Ok(())
46}
47
48fn output_progress(stderr: &mut Stderr, bytes: usize, elapsed: String, rate: f64) {
49    let bytes = style::style(format!("{} ", bytes)).with(Color::Red);
50    let elapsed = style::style(elapsed).with(Color::Green);
51    let rate = style::style(format!("[{:.0}b/s]", rate)).with(Color::Blue);
52    let _ = execute!(
53        stderr,
54        cursor::MoveToColumn(0),
55        Clear(ClearType::CurrentLine),
56        PrintStyledContent(bytes),
57        PrintStyledContent(elapsed),
58        PrintStyledContent(rate),
59    );
60    let _ = stderr.flush();
61}
62
63/// The TimeOutput trait adds a `as_time()` method to `u64`
64///
65/// # Example
66/// Here is an example of how to use it.
67///
68/// ```rust
69/// use flo_pv::stats::TimeOutput;
70/// assert_eq!(65_u64.as_time(), String::from("0:01:05"));
71/// ```
72pub trait TimeOutput {
73    fn as_time(&self) -> String;
74}
75
76impl TimeOutput for u64 {
77    /// Renders the u64 into a time string.
78    fn as_time(&self) -> String {
79        let (hours, left) = (*self / 3600, *self % 3600);
80        let (minutes, seconds) = (left / 60, left % 60);
81        format!("{}:{:02}:{:02}", hours, minutes, seconds)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::TimeOutput;
88
89    #[test]
90    fn as_time_format() {
91        let pairs = vec![
92            (5_u64, "0:00:05"),
93            (60_u64, "0:01:00"),
94            (154_u64, "0:02:34"),
95            (3603_u64, "1:00:03"),
96        ];
97        for (input, output) in pairs {
98            assert_eq!(input.as_time().as_str(), output)
99        }
100    }
101}