aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_cli/src/progress_report.rs
blob: 31867a1e973e3f29fa00f120619fba8bf1366900 (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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! A simple progress bar
//!
//! A single thread non-optimized progress bar
use std::io::Write;

/// A Simple ASCII Progress Bar
pub struct ProgressReport {
    curr: f32,
    text: String,
    hidden: bool,

    len: u64,
    pos: u64,
    msg: String,
}

impl ProgressReport {
    pub fn new(len: u64) -> ProgressReport {
        ProgressReport {
            curr: 0.0,
            text: String::new(),
            hidden: false,
            len,
            pos: 0,
            msg: String::new(),
        }
    }

    pub fn hidden() -> ProgressReport {
        ProgressReport {
            curr: 0.0,
            text: String::new(),
            hidden: true,
            len: 0,
            pos: 0,
            msg: String::new(),
        }
    }

    pub fn set_message(&mut self, msg: &str) {
        self.msg = msg.to_string();
        self.tick();
    }

    pub fn println<I: Into<String>>(&mut self, msg: I) {
        self.clear();
        println!("{}", msg.into());
        self.tick();
    }

    pub fn inc(&mut self, delta: u64) {
        self.pos += delta;
        if self.len == 0 {
            self.set_value(0.0)
        } else {
            self.set_value((self.pos as f32) / (self.len as f32))
        }
        self.tick();
    }

    pub fn finish_and_clear(&mut self) {
        self.clear();
    }

    pub fn tick(&mut self) {
        if self.hidden {
            return;
        }
        let percent = (self.curr * 100.0) as u32;
        let text = format!("{}/{} {:3>}% {}", self.pos, self.len, percent, self.msg);
        self.update_text(&text);
    }

    fn update_text(&mut self, text: &str) {
        // Get length of common portion
        let mut common_prefix_length = 0;
        let common_length = usize::min(self.text.len(), text.len());

        while common_prefix_length < common_length
            && text.chars().nth(common_prefix_length).unwrap()
                == self.text.chars().nth(common_prefix_length).unwrap()
        {
            common_prefix_length += 1;
        }

        // Backtrack to the first differing character
        let mut output = String::new();
        output += &'\x08'.to_string().repeat(self.text.len() - common_prefix_length);
        // Output new suffix
        output += &text[common_prefix_length..text.len()];

        // If the new text is shorter than the old one: delete overlapping characters
        if let Some(overlap_count) = self.text.len().checked_sub(text.len()) {
            if overlap_count > 0 {
                output += &" ".repeat(overlap_count);
                output += &"\x08".repeat(overlap_count);
            }
        }

        let _ = std::io::stdout().write(output.as_bytes());
        let _ = std::io::stdout().flush();
        self.text = text.to_string();
    }

    fn set_value(&mut self, value: f32) {
        self.curr = f32::max(0.0, f32::min(1.0, value));
    }

    fn clear(&mut self) {
        if self.hidden {
            return;
        }

        // Fill all last text to space and return the cursor
        let spaces = " ".repeat(self.text.len());
        let backspaces = "\x08".repeat(self.text.len());
        print!("{}{}{}", backspaces, spaces, backspaces);
        self.text = String::new();
    }
}