summaryrefslogtreecommitdiff
path: root/src/progress_bar.rs
blob: 97c8ad9074fe9141532976a0cca2e3e1880dfb97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use anyhow::{bail, Result};
use ratatui::text::{Line, Span};
use std::fmt::Write;

const PREFIX: &str = "Progress: [";
const PREFIX_WIDTH: u16 = PREFIX.len() as u16;
// Leaving the last char empty (_) for `total` > 99.
const POSTFIX_WIDTH: u16 = "] xxx/xx exercises_".len() as u16;
const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH;
const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4;

const PROGRESS_EXCEEDS_MAX_ERR: &str =
    "The progress of the progress bar is higher than the maximum";

pub fn progress_bar(progress: u16, total: u16, line_width: u16) -> Result<String> {
    use crossterm::style::Stylize;

    if progress > total {
        bail!(PROGRESS_EXCEEDS_MAX_ERR);
    }

    if line_width < MIN_LINE_WIDTH {
        return Ok(format!("Progress: {progress}/{total} exercises"));
    }

    let mut line = String::with_capacity(usize::from(line_width));
    line.push_str(PREFIX);

    let width = line_width - WRAPPER_WIDTH;
    let filled = (width * progress) / total;

    let mut green_part = String::with_capacity(usize::from(filled + 1));
    for _ in 0..filled {
        green_part.push('#');
    }

    if filled < width {
        green_part.push('>');
    }
    write!(line, "{}", green_part.green()).unwrap();

    let width_minus_filled = width - filled;
    if width_minus_filled > 1 {
        let red_part_width = width_minus_filled - 1;
        let mut red_part = String::with_capacity(usize::from(red_part_width));
        for _ in 0..red_part_width {
            red_part.push('-');
        }
        write!(line, "{}", red_part.red()).unwrap();
    }

    write!(line, "] {progress:>3}/{total} exercises").unwrap();

    Ok(line)
}

pub fn progress_bar_ratatui(progress: u16, total: u16, line_width: u16) -> Result<Line<'static>> {
    use ratatui::style::Stylize;

    if progress > total {
        bail!(PROGRESS_EXCEEDS_MAX_ERR);
    }

    if line_width < MIN_LINE_WIDTH {
        return Ok(Line::raw(format!("Progress: {progress}/{total} exercises")));
    }

    let mut spans = Vec::with_capacity(4);
    spans.push(Span::raw(PREFIX));

    let width = line_width - WRAPPER_WIDTH;
    let filled = (width * progress) / total;

    let mut green_part = String::with_capacity(usize::from(filled + 1));
    for _ in 0..filled {
        green_part.push('#');
    }

    if filled < width {
        green_part.push('>');
    }
    spans.push(green_part.green());

    let width_minus_filled = width - filled;
    if width_minus_filled > 1 {
        let red_part_width = width_minus_filled - 1;
        let mut red_part = String::with_capacity(usize::from(red_part_width));
        for _ in 0..red_part_width {
            red_part.push('-');
        }
        spans.push(red_part.red());
    }

    spans.push(Span::raw(format!("] {progress:>3}/{total} exercises")));

    Ok(Line::from(spans))
}