diff options
Diffstat (limited to 'crates/ra_cli/src/progress_report.rs')
-rw-r--r-- | crates/ra_cli/src/progress_report.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/crates/ra_cli/src/progress_report.rs b/crates/ra_cli/src/progress_report.rs new file mode 100644 index 000000000..31867a1e9 --- /dev/null +++ b/crates/ra_cli/src/progress_report.rs | |||
@@ -0,0 +1,120 @@ | |||
1 | //! A simple progress bar | ||
2 | //! | ||
3 | //! A single thread non-optimized progress bar | ||
4 | use std::io::Write; | ||
5 | |||
6 | /// A Simple ASCII Progress Bar | ||
7 | pub struct ProgressReport { | ||
8 | curr: f32, | ||
9 | text: String, | ||
10 | hidden: bool, | ||
11 | |||
12 | len: u64, | ||
13 | pos: u64, | ||
14 | msg: String, | ||
15 | } | ||
16 | |||
17 | impl ProgressReport { | ||
18 | pub fn new(len: u64) -> ProgressReport { | ||
19 | ProgressReport { | ||
20 | curr: 0.0, | ||
21 | text: String::new(), | ||
22 | hidden: false, | ||
23 | len, | ||
24 | pos: 0, | ||
25 | msg: String::new(), | ||
26 | } | ||
27 | } | ||
28 | |||
29 | pub fn hidden() -> ProgressReport { | ||
30 | ProgressReport { | ||
31 | curr: 0.0, | ||
32 | text: String::new(), | ||
33 | hidden: true, | ||
34 | len: 0, | ||
35 | pos: 0, | ||
36 | msg: String::new(), | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub fn set_message(&mut self, msg: &str) { | ||
41 | self.msg = msg.to_string(); | ||
42 | self.tick(); | ||
43 | } | ||
44 | |||
45 | pub fn println<I: Into<String>>(&mut self, msg: I) { | ||
46 | self.clear(); | ||
47 | println!("{}", msg.into()); | ||
48 | self.tick(); | ||
49 | } | ||
50 | |||
51 | pub fn inc(&mut self, delta: u64) { | ||
52 | self.pos += delta; | ||
53 | if self.len == 0 { | ||
54 | self.set_value(0.0) | ||
55 | } else { | ||
56 | self.set_value((self.pos as f32) / (self.len as f32)) | ||
57 | } | ||
58 | self.tick(); | ||
59 | } | ||
60 | |||
61 | pub fn finish_and_clear(&mut self) { | ||
62 | self.clear(); | ||
63 | } | ||
64 | |||
65 | pub fn tick(&mut self) { | ||
66 | if self.hidden { | ||
67 | return; | ||
68 | } | ||
69 | let percent = (self.curr * 100.0) as u32; | ||
70 | let text = format!("{}/{} {:3>}% {}", self.pos, self.len, percent, self.msg); | ||
71 | self.update_text(&text); | ||
72 | } | ||
73 | |||
74 | fn update_text(&mut self, text: &str) { | ||
75 | // Get length of common portion | ||
76 | let mut common_prefix_length = 0; | ||
77 | let common_length = usize::min(self.text.len(), text.len()); | ||
78 | |||
79 | while common_prefix_length < common_length | ||
80 | && text.chars().nth(common_prefix_length).unwrap() | ||
81 | == self.text.chars().nth(common_prefix_length).unwrap() | ||
82 | { | ||
83 | common_prefix_length += 1; | ||
84 | } | ||
85 | |||
86 | // Backtrack to the first differing character | ||
87 | let mut output = String::new(); | ||
88 | output += &'\x08'.to_string().repeat(self.text.len() - common_prefix_length); | ||
89 | // Output new suffix | ||
90 | output += &text[common_prefix_length..text.len()]; | ||
91 | |||
92 | // If the new text is shorter than the old one: delete overlapping characters | ||
93 | if let Some(overlap_count) = self.text.len().checked_sub(text.len()) { | ||
94 | if overlap_count > 0 { | ||
95 | output += &" ".repeat(overlap_count); | ||
96 | output += &"\x08".repeat(overlap_count); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | let _ = std::io::stdout().write(output.as_bytes()); | ||
101 | let _ = std::io::stdout().flush(); | ||
102 | self.text = text.to_string(); | ||
103 | } | ||
104 | |||
105 | fn set_value(&mut self, value: f32) { | ||
106 | self.curr = f32::max(0.0, f32::min(1.0, value)); | ||
107 | } | ||
108 | |||
109 | fn clear(&mut self) { | ||
110 | if self.hidden { | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | // Fill all last text to space and return the cursor | ||
115 | let spaces = " ".repeat(self.text.len()); | ||
116 | let backspaces = "\x08".repeat(self.text.len()); | ||
117 | print!("{}{}{}", backspaces, spaces, backspaces); | ||
118 | self.text = String::new(); | ||
119 | } | ||
120 | } | ||