mtlog_progress/
lib.rs

1
2//! # mtlog-progress
3//! A progress bar implementation working gracefully with mtlog's logger.
4//!
5//! ## Usage with std threads
6//! ```toml
7//! // Cargo.toml
8//! ...
9//! [dependencies]
10//! mtlog-progress = "0.1.0"
11//! mtlog = "0.1.4"
12//! ```
13//! 
14//! ```rust
15//! use mtlog::logger_config;
16//! use mtlog_progress::LogProgressBar;
17//! 
18//! logger_config()
19//!     .init_global();
20//! 
21//! let h = std::thread::spawn(|| {
22//!     let pb = LogProgressBar::new(100, "My Progress Bar");
23//!     for i in 0..100 {
24//!         pb.inc(1);
25//!         if i == 50 {
26//!             log::info!("Halfway there!");
27//!         }
28//!     }
29//!     pb.finish();
30//! });
31//! log::info!("This log goes below the progress bar");
32//! h.join().unwrap(); // the progress bar continue to work at it's line position
33//! 
34//! ```
35//! ## Usage with tokio tasks
36//! 
37//! ## Usage
38//! ```toml
39//! // Cargo.toml
40//! ...
41//! [dependencies]
42//! mtlog-progress = "0.1.0"
43//! mtlog-tokio = "0.1.0"
44//! tokio = { version = "1.40.0", features = ["full"] }
45//! ```
46//! 
47//! ```rust
48//! use mtlog_tokio::logger_config;
49//! use mtlog_progress::LogProgressBar;
50//! 
51//! #[tokio::main]
52//! async fn main() {
53//!     logger_config()
54//!         .scope_global(async move {
55//!             let h = tokio::spawn(async move {
56//!                 logger_config()
57//!                     .scope_local(async move {
58//!                         let pb = LogProgressBar::new(100, "My Progress Bar");
59//!                         for i in 0..100 {
60//!                             pb.inc(1);
61//!                             if i == 50 {
62//!                                 log::info!("Halfway there!");
63//!                             }
64//!                         }
65//!                         pb.finish();
66//!                     }).await;    
67//!             });
68//!             log::info!("This log goes below the progress bar");
69//!             h.await.unwrap(); // the progress bar continue to work at it's line position
70//!         }).await;
71//! }
72//! ```
73
74
75use std::sync::{Arc, Mutex};
76use colored::Colorize;
77use uuid::Uuid;
78
79
80#[derive(Clone)]
81pub struct LogProgressBar {
82    n_iter: Arc<usize>,
83    name: Arc<str>,
84    current_iter: Arc<Mutex<usize>>,
85    id: Arc<Uuid>,
86    finished: Arc<Mutex<bool>>
87}
88
89impl LogProgressBar {
90    pub fn new(n_iter: usize, name: &str) -> Self {
91        let pb = Self {
92            n_iter: Arc::new(n_iter.max(1)),
93            name: name.into(),
94            current_iter: Arc::new(Mutex::new(0usize)),
95            id: Arc::new(Uuid::new_v4()),
96            finished: Arc::new(Mutex::new(false))
97        };
98        pb.send();
99        pb
100    }
101
102    pub fn send(&self) {
103        if *self.finished.lock().unwrap() {
104            log::info!("___PROGRESS___{}___FINISHED",self.id)
105        } else {
106            log::info!("___PROGRESS___{}___{}",self.id,self.format())
107        }
108    }
109
110    pub fn set_progress(&self, n: usize) {
111        *self.current_iter.lock().unwrap() = n;
112        self.send();
113    }
114
115    pub fn inc(&self, n: usize) {
116        *self.current_iter.lock().unwrap() += n;
117        self.send();
118    }
119
120    fn format(&self) -> String {
121        let current_iter = *self.current_iter.lock().unwrap();
122        let percentage = (current_iter as f64 / *self.n_iter as f64 * 100.0) as usize;
123        let bar_length = 20; // Length of the progress bar
124        let filled_length = (bar_length * current_iter / *self.n_iter).min(bar_length);
125        let bar = "#".repeat(filled_length) + &".".repeat(bar_length - filled_length);
126        let n_iter_str = self.n_iter.to_string();
127        format!(
128            "Progress {name}: [{bar}] {current:>len$}/{n_iter_str} {percentage:>3}%",
129            name=self.name.cyan(), 
130            bar=bar.cyan(),
131            current=current_iter,
132            len=n_iter_str.len(),
133        )
134    }
135    
136    pub fn finish(&self) {
137        if *self.finished.lock().unwrap() {
138            return
139        }
140        *self.finished.lock().unwrap() = true;    
141        *self.current_iter.lock().unwrap() = *self.n_iter;
142        self.send();
143    }
144}
145
146impl Drop for LogProgressBar {
147    fn drop(&mut self) {
148        *self.finished.lock().unwrap() = true;
149        self.send();
150    }
151}
152
153
154#[test]
155fn test_progress_bar() {
156    use mtlog::logger_config;
157    logger_config()
158        .init_global();
159    let pb = LogProgressBar::new(100, "Test");
160    for _ in 0..50 {
161        pb.inc(1);
162    }
163    pb.finish();
164    std::thread::sleep(std::time::Duration::from_millis(1));
165}