multi_progressbar/lib.rs
1//! # multi-progress
2//!
3//! multi-progress is a library to show multiple progress bars along with log outputs in terminal.
4//!
5//! ## Usage
6//!
7//! 1. Implement [TaskProgress] trait for your task.
8//! 2. Call [MultiProgressBar::new] with a [ProgressBar] implementation (provided in the [bar] module).
9//! 3. Call [MultiProgressBar::draw] to draw progress bars when needed.
10//!
11//! ```rust
12//! use multi_progressbar::{
13//! MultiProgressBar, TaskProgress,
14//! bar::classic::ClassicProgressBar
15//! };
16//!
17//! struct Task {
18//! name: String,
19//! progress: u64,
20//! total: u64,
21//! }
22//!
23//! impl TaskProgress for Task {
24//! fn progress(&self) -> (u64, u64) {
25//! (self.progress, self.total)
26//! }
27//! fn after(&self) -> Option<String> {
28//! Some(format!("{}/{} completed", self.progress, self.total))
29//! }
30//! fn before(&self) -> Option<String> {
31//! Some(self.name.clone())
32//! }
33//! }
34//!
35//! let mp = MultiProgressBar::new(ClassicProgressBar::new());
36//! let task1 = Task {
37//! name: "task1".to_string(),
38//! progress: 0,
39//! total: 100,
40//! };
41//! let task2 = Task {
42//! name: "task2".to_string(),
43//! progress: 33,
44//! total: 100,
45//! };
46//! let tasks = vec![task1, task2];
47//! mp.draw(&tasks).unwrap();
48//!
49//!
50//! ```
51
52#![warn(missing_docs)]
53
54use crossterm::{cursor, queue, terminal};
55use std::io::Write;
56
57/// bar module contains premade progress bar styles.
58pub mod bar;
59
60/// Task is abstraction for one single running task.
61pub trait TaskProgress {
62 /// returns the current progress and total progress.
63 fn progress(&self) -> (u64, u64);
64 /// returns message to show before progress bar
65 fn before(&self) -> Option<String> {
66 None
67 }
68 /// returns message to show after progress bar
69 fn after(&self) -> Option<String> {
70 None
71 }
72}
73
74/// ProgressBar is an abstraction for the appearance of a progress bar.
75pub trait ProgressBar {
76 /// Progress is provided by TaskProgress.
77 type Task: TaskProgress;
78 /// formats a line of progress bar to show in terminal.
79 fn format_line(&self, progress: &Self::Task, width: usize) -> String;
80}
81
82/// MultiProgress is the main struct of this library.
83/// It handles drawing progress bars and log outputs.
84pub struct MultiProgressBar<P: ProgressBar> {
85 progress_bar: P,
86}
87
88impl<P: ProgressBar> MultiProgressBar<P> {
89 /// creates a new MultiProgress with given ProgressBar style.
90 pub fn new(progress_bar: P) -> Self {
91 MultiProgressBar { progress_bar }
92 }
93
94 /// logs a message above progress bars.
95 pub fn log(&self, msg: &str, ntasks: usize) -> std::io::Result<()> {
96 let (width, height) = crossterm::terminal::size().unwrap();
97 let mut stdout = std::io::stdout();
98
99 queue!(
100 stdout,
101 cursor::MoveToRow(height - ntasks as u16 - 1),
102 cursor::MoveToColumn(0),
103 terminal::ScrollUp(1),
104 )?;
105
106 write!(stdout, "{:width$}", msg, width = width as usize)
107 }
108
109 /// draws the progress bars.
110 pub fn draw(&self, tasks: &[P::Task]) -> std::io::Result<()> {
111 let (width, height) = crossterm::terminal::size().unwrap();
112 let mut stdout = std::io::stdout();
113 queue!(
114 stdout,
115 terminal::BeginSynchronizedUpdate,
116 cursor::MoveToColumn(0),
117 cursor::MoveToRow(height - tasks.len() as u16 - 1),
118 )?;
119
120 for task in tasks {
121 let line = self.progress_bar.format_line(task, width as usize);
122 queue!(stdout, cursor::MoveToColumn(0), cursor::MoveDown(1))?;
123 write!(stdout, "{}", line)?;
124 }
125
126 queue!(
127 stdout,
128 terminal::EndSynchronizedUpdate,
129 cursor::MoveToColumn(0),
130 )?;
131
132 stdout.flush()?;
133
134 Ok(())
135 }
136}