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}