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