counter_cli/
basic_counters.rs

1use std::io::{self, Write};
2
3use crossterm::cursor;
4
5use crate::loading_bar::LoadingBar;
6
7pub struct BasicCounter {
8    total: Option<u32>,
9    current: u32,
10    avg: Option<f32>,
11    buffer: String,
12}
13
14impl BasicCounter {
15    pub fn new(total: Option<u32>) -> Self {
16        Self {
17            total,
18            current: 0,
19            avg: None,
20            buffer: " ".repeat(crossterm::terminal::size().unwrap_or((2, 0)).0 as usize -2),
21        }
22    }
23}
24
25
26impl BasicCounter {
27    /// Updates the counter by the given amount and returns whether the counter is full\
28    pub fn update(&mut self, s_elapsed: f32, increment: u32) -> bool {
29        let clamped = increment.clamp(0, self.total.unwrap_or(u32::MAX) - self.current);
30        self.current += clamped;
31        self.avg = match self.avg {
32            Some(avg) => Some((avg + clamped as f32 / s_elapsed) / 2.0),
33            None => Some(clamped as f32 / s_elapsed),
34        }; self.current == self.total.unwrap_or(u32::MAX)
35    }
36
37    /// Draws the counter as raw data
38    pub fn draw_data(&self, description: &str) {
39        let mut stdout = io::stdout();
40
41        let percent = self.total.map(|total| self.current as f32 * 100.0 / total as f32);
42        let eta = self.total.map(|total| (total - self.current) as f32 / self.avg.unwrap_or(1.0));
43
44        // wipe the lines that were previously drawn
45        print!("{}{}{}{}", cursor::MoveToColumn(0), self.buffer, cursor::MoveToColumn(0), cursor::Hide);
46
47        write!(
48            stdout,
49            "\x1b[36;1minfo: \x1b[0m{description}... \x1b[34m( \x1b[33m{}{}{}\x1b[34m )",
50            match self.total {
51                Some(total) => format!("{}\x1b[34m/\x1b[33m{total}", self.current),
52                None => self.current.to_string(),
53            },
54            match self.avg {
55                Some(avg) => format!("\x1b[34m, \x1b[33m{}", LoadingBar::right_avg_unit(avg)),            
56                None => String::new(),
57            },
58            match self.total {
59                Some(_) => format!("\x1b[34m, \x1b[33m{:.2}%{}", percent.unwrap(), match eta {
60                    Some(eta) => format!("\x1b[34m, \x1b[36;1meta: \x1b[0m\x1b[33m{}", LoadingBar::right_time_unit(eta)),
61                    None => String::new(),
62                }),
63                None => String::new(),
64            },
65        ).unwrap();
66
67        stdout.flush().unwrap();
68    }
69
70    /// Draws the data as tally marks
71    pub fn draw_tally(&self, description: &str) {
72        self.draw_data(description);
73
74        // wipe the lines that were previously drawn
75        print!("\n{}{}{}", cursor::MoveToColumn(0), self.buffer, cursor::MoveToColumn(0));
76
77        print!(
78            "    > \x1b[33;1m{}{}{}\x1b[0m",
79            "𝍸 ".repeat(self.current as usize / 5),
80            "𝍷".repeat(self.current as usize % 5),
81            cursor::MoveUp(1),
82        ); io::stdout().flush().unwrap();
83    }
84}