progress/lib.rs
1#![crate_name = "progress"]
2#![crate_type = "rlib"]
3#![crate_type = "dylib"]
4
5//! **progress** is meant to be a set of useful tools for showing program running
6//! progress (as its name) and steps.
7//!
8//! Installation
9//! ============
10//!
11//! Add the following lines to your `Cargo.toml` dependencies section, if you
12//! use [Cargo](https://crates.io):
13//!
14//! ```
15//! [dependencies]
16//! progress = "0.1.0"
17//! ```
18//!
19//! Usage
20//! =====
21//!
22//! Please check documentations for each structs. Life is easy here :)
23//!
24//! Who create this
25//! ===============
26//!
27//! - [Ying-Ruei Liang (KK)](https://github.com/TheKK)
28//!
29//! Contribution
30//! ============
31//!
32//! I can't believe you would say that, but if you have any great idea or any
33//! bug report, don't be hesitate! It would be more wonderful if someone wants
34//! to write some code for this project!
35//!
36//! TODO list
37//! =========
38//!
39//! - BarBuilder, so we can do some customization, e.g. change the symbols used
40//! - Add more type of indicators, e.g. spinning symbol or nayn cat :3
41//! - Color/styled text support (print!("{:<50}") will count unprintable text as
42//! well, I have to solve it first)
43//! - Make output format customizable, despite I have no idea how to achieve this
44//! for now.
45//!
46//! License
47//! =======
48//!
49//! MIT
50
51use std::io::{self, Write};
52
53extern crate terminal_size;
54use terminal_size::{terminal_size, Width};
55
56/// A builder that used for creating customize progress bar.
57///
58/// # Examples
59///
60/// ```
61/// use std::thread;
62///
63/// extern crate progress;
64///
65/// fn main() {
66/// let mut bar = progress::BarBuilder::new()
67/// .left_cap("<")
68/// .right_cap(">")
69/// .empty_symbol("-")
70/// .filled_symbol("/")
71/// .build();
72///
73/// bar.set_job_title("Meow...");
74///
75/// for i in 0..11 {
76/// thread::sleep_ms(500);
77/// bar.reach_percent(i * 10);
78/// }
79/// }
80pub struct BarBuilder {
81 _left_cap: Option<String>,
82 _right_cap: Option<String>,
83 _filled_symbol: Option<String>,
84 _empty_symbol: Option<String>,
85}
86
87impl BarBuilder {
88 /// Create a new progress bar builder.
89 pub fn new() -> BarBuilder {
90 BarBuilder {
91 _left_cap: None,
92 _right_cap: None,
93 _filled_symbol: None,
94 _empty_symbol: None,
95 }
96 }
97
98 /// Set desired symbol used as left cap
99 ///
100 /// ```shell
101 /// [=========-] 90%
102 /// ^
103 pub fn left_cap(&mut self, symbol: &str) -> &mut BarBuilder {
104 self._left_cap = Some(symbol.to_string());
105
106 self
107 }
108
109 /// Set desired symbol used as right cap
110 ///
111 /// ```shell
112 /// [=========-] 90%
113 /// ^
114 pub fn right_cap(&mut self, symbol: &str) -> &mut BarBuilder {
115 self._right_cap = Some(symbol.to_string());
116
117 self
118 }
119
120 /// Set desired symbol used as filled bar
121 ///
122 /// ```shell
123 /// [=========-] 90%
124 /// ^^^^^^^^^
125 pub fn filled_symbol(&mut self, symbol: &str) -> &mut BarBuilder {
126 self._filled_symbol = Some(symbol.to_string());
127
128 self
129 }
130
131 /// Set desired symbol used as empty bar
132 ///
133 /// ```shell
134 /// [=========-] 90%
135 /// ^
136 /// ```
137 pub fn empty_symbol(&mut self, symbol: &str) -> &mut BarBuilder {
138 self._empty_symbol = Some(symbol.to_string());
139
140 self
141 }
142
143 /// Build progress bar according to previous configurations.
144 pub fn build(&mut self) -> Bar {
145 // XXX Does `take()` appropriate way?
146 Bar {
147 _job_title: String::new(),
148 _progress_percentage: 0,
149 _left_cap: self._left_cap.take().unwrap_or(String::from("[")),
150 _right_cap: self._right_cap.take().unwrap_or(String::from("]")),
151 _filled_symbol: self._filled_symbol.take().unwrap_or(String::from("=")),
152 _empty_symbol: self._empty_symbol.take().unwrap_or(String::from("-")),
153 }
154 }
155}
156
157/// Struct that used for presenting progress bar with plain texts.
158///
159/// It looks like:
160///
161/// ```shell
162/// Doing something [===-------] 70%
163/// ```
164///
165/// # Examples
166///
167/// ```
168/// use std::thread;
169///
170/// extern crate progress;
171///
172/// fn main() {
173/// let bar = progress::Bar::new();
174///
175/// bar.set_job_title("Working...");
176///
177/// for i in 0..11 {
178/// thread::sleep_ms(100);
179/// bar.reach_percent(i * 10);
180/// }
181/// }
182pub struct Bar {
183 _job_title: String,
184 _progress_percentage: i32,
185 _left_cap: String,
186 _right_cap: String,
187 _filled_symbol: String,
188 _empty_symbol: String,
189}
190
191impl Bar {
192 /// Create a new progress bar.
193 pub fn new() -> Bar {
194 Bar {
195 _job_title: String::new(),
196 _progress_percentage: 0,
197 _left_cap: String::from("["),
198 _right_cap: String::from("]"),
199 _filled_symbol: String::from("="),
200 _empty_symbol: String::from("-"),
201 }
202 }
203
204 /// Reset progress percentage to zero and job title to empty string. Also
205 /// prints "\n".
206 pub fn jobs_done(&mut self) {
207 self._job_title.clear();
208 self._progress_percentage = 0;
209
210 print!("\n");
211 }
212
213 /// Set text shown in progress bar.
214 pub fn set_job_title(&mut self, new_title: &str) {
215 self._job_title.clear();
216 self._job_title.push_str(new_title);
217 self._show_progress();
218 }
219
220 /// Put progress to given percentage.
221 pub fn reach_percent(&mut self, percent: i32) {
222 self._progress_percentage = percent;
223 self._show_progress();
224 }
225
226 /// Increase progress with given percentage.
227 pub fn add_percent(&mut self, progress: i32) {
228 self._progress_percentage += progress;
229 self._show_progress();
230 }
231}
232
233impl Bar {
234 fn _show_progress(&self) {
235 let width = if let Some((Width(w), _)) = terminal_size() {
236 w as i32
237 } else {
238 81 as i32
239 };
240 let overhead = self._progress_percentage / 100;
241 let left_percentage = self._progress_percentage - overhead * 100;
242 let bar_len = width - (50 + 5) - 2;
243 let bar_finished_len = ((bar_len as f32) *
244 (left_percentage as f32 / 100.0)) as i32;
245 let filled_symbol = if overhead & 0b1 == 0 {
246 &self._filled_symbol
247 } else {
248 &self._empty_symbol
249 };
250 let empty_symbol = if overhead & 0b1 == 0 {
251 &self._empty_symbol
252 } else {
253 &self._filled_symbol
254 };
255
256 io::stdout().flush().unwrap();
257 print!("\r");
258
259 print!("{:<50}", self._job_title);
260 print!("{}", self._left_cap);
261 for _ in 0..bar_finished_len {
262 print!("{}", filled_symbol);
263 }
264 for _ in bar_finished_len..bar_len {
265 print!("{}", empty_symbol);
266 }
267 print!("{}", self._right_cap);
268 print!("{:>4}%", self._progress_percentage);
269 }
270}
271
272/// Struct that used for presenting progress with plain texts.
273///
274/// It looks like:
275///
276/// ```shell
277/// Doing something
278/// ```
279///
280/// # Examples
281///
282/// ```
283/// use std::thread;
284///
285/// extern crate progress;
286///
287/// fn main() {
288/// let mut text = progress::Text::new();
289///
290/// text.set_job_title("Drawing...");
291/// thread::sleep_ms(1000);
292///
293/// text.set_job_title("Painting...");
294/// thread::sleep_ms(1000);
295///
296/// text.set_job_title("Sleeping zzz");
297/// thread::sleep_ms(1000);
298///
299/// text.set_job_title("Wait! Is that a nyan cat?");
300/// thread::sleep_ms(1000);
301///
302/// text.set_job_title("This must be my dream zzzzzz");
303/// thread::sleep_ms(1000);
304///
305/// text.jobs_done();
306/// }
307pub struct Text {
308 _job_title: String,
309}
310
311impl Text {
312 /// Create a new progress text.
313 pub fn new() -> Text {
314 Text {
315 _job_title: String::new(),
316 }
317 }
318
319 /// Set text shown in progress text.
320 pub fn set_job_title(&mut self, new_title: &str) {
321 self._job_title.clear();
322 self._job_title.push_str(new_title);
323 self._show_progress();
324 }
325
326 /// Tell progress::Text everything has been done. Also prints "\n".
327 pub fn jobs_done(&self) {
328 print!("\n");
329 }
330}
331
332impl Text {
333 fn _show_progress(& self) {
334 io::stdout().flush().unwrap();
335 print!("\r");
336 // TODO How to handle extra text?
337 print!("{:<81}", self._job_title);
338 }
339}
340
341/// Struct that used for presenting progress with plain texts.
342///
343/// It looks like:
344///
345/// ```shell
346/// * Doing something
347/// / Doing another thing
348/// ```
349///
350/// # Examples
351///
352/// ```
353/// use std::thread;
354///
355/// extern crate progress;
356///
357/// fn main() {
358/// let mut spinningCircle = progress::SpinningCircle::new();
359///
360/// spinningCircle.set_job_title("Writing boring and stupid homeworks");
361/// for _ in 0..50 {
362/// thread::sleep_ms(50);
363/// spinningCircle.tick();
364/// }
365/// spinningCircle.jobs_done();
366///
367/// spinningCircle.set_job_title("Previewing boring and stupid subjects");
368/// for _ in 0..50 {
369/// thread::sleep_ms(50);
370/// spinningCircle.tick();
371/// }
372/// spinningCircle.jobs_done();
373///
374/// spinningCircle.set_job_title("Learning and creating interesting programs");
375/// for _ in 0..50 {
376/// thread::sleep_ms(50);
377/// spinningCircle.tick();
378/// }
379/// spinningCircle.jobs_done();
380/// }
381pub struct SpinningCircle {
382 _job_title: String,
383 _circle_symbols: Vec<char>,
384 _finished_symbol: char,
385 _tick_count: usize,
386}
387
388impl SpinningCircle {
389 /// Create a new progress spinning circle.
390 pub fn new() -> SpinningCircle {
391 SpinningCircle {
392 _job_title: String::new(),
393 _circle_symbols: vec!['|', '/', '-', '\\'],
394 _finished_symbol: '*',
395 _tick_count: 0,
396 }
397 }
398
399 /// Set text shown in progress spinning circle.
400 pub fn set_job_title(&mut self, new_title: &str) {
401 self._job_title.clear();
402 self._job_title.push_str(new_title);
403 self._show_progress();
404 }
405
406 /// Tell spinning circle to spin a bit.
407 pub fn tick(&mut self) {
408 self._tick_count += 1;
409 self._show_progress();
410 }
411
412 /// Print finished symbol at the position spinning circle symbol used to be.
413 /// And print "\n" to jump to next line.
414 ///
415 /// e.g.
416 /// * Collection kitties
417 pub fn jobs_done(& self) {
418 self._show_finished();
419 }
420}
421
422impl SpinningCircle {
423 fn _print_symbol_and_texts(& self, symbol: &char) {
424 io::stdout().flush().unwrap();
425 print!("\r");
426 print!("{}", symbol);
427 // TODO How to handle extra text?
428 print!(" {:<81}", self._job_title);
429 }
430
431 fn _show_progress(& self) {
432 let circle_symbol: &char = self._circle_symbols.get(
433 self._tick_count % self._circle_symbols.len()).unwrap();
434
435 self._print_symbol_and_texts(circle_symbol);
436 }
437
438 fn _show_finished(& self) {
439 self._print_symbol_and_texts(&self._finished_symbol);
440 print!("\n");
441 }
442}