1
2use std::{sync::{Arc, Mutex},time::{Duration, Instant}};
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 min_duration: Arc<Duration>,
88 last_iter: Arc<Mutex<Instant>>
89}
90
91impl LogProgressBar {
92 pub fn new(n_iter: usize, name: &str) -> Self {
93 let pb = Self {
94 n_iter: Arc::new(n_iter.max(1)),
95 name: name.into(),
96 current_iter: Arc::new(Mutex::new(0usize)),
97 id: Arc::new(Uuid::new_v4()),
98 finished: Arc::new(Mutex::new(false)),
99 min_duration: Arc::new(Duration::from_millis(100)),
100 last_iter: Arc::new(Mutex::new(Instant::now()-Duration::from_millis(100))),
101 };
102 pb.send();
103 pb
104 }
105
106 pub fn with_min_timestep_ms(mut self, min_duration_ms: f64) -> Self {
107 self.min_duration = Arc::new(Duration::from_micros((min_duration_ms*1000.0).round() as u64));
108 self
109 }
110
111 pub fn send(&self) {
112 if !*self.finished.lock().unwrap() && self.last_iter.lock().unwrap().elapsed() > *self.min_duration {
113 log::info!("___PROGRESS___{}___{}",self.id,self.format());
114 *self.last_iter.lock().unwrap() = Instant::now();
115 }
116 }
117
118 pub fn set_progress(&self, n: usize) {
119 *self.current_iter.lock().unwrap() = n;
120 self.send();
121 }
122
123 pub fn inc(&self, n: usize) {
124 *self.current_iter.lock().unwrap() += n;
125 self.send();
126 }
127
128 fn format(&self) -> String {
129 let current_iter = *self.current_iter.lock().unwrap();
130 let percentage = (current_iter as f64 / *self.n_iter as f64 * 100.0) as usize;
131 let bar_length = 20; let filled_length = (bar_length * current_iter / *self.n_iter).min(bar_length);
133 let bar = "#".repeat(filled_length) + &".".repeat(bar_length - filled_length);
134 let n_iter_str = self.n_iter.to_string();
135 format!(
136 "Progress {name}: [{bar}] {current:>len$}/{n_iter_str} {percentage:>3}%",
137 name=self.name.cyan(),
138 bar=bar.cyan(),
139 current=current_iter,
140 len=n_iter_str.len(),
141 )
142 }
143
144 pub fn finish(&self) {
145 if *self.finished.lock().unwrap() {
146 return
147 }
148 self.set_progress(*self.n_iter);
149 *self.finished.lock().unwrap() = true;
150 log::info!("___PROGRESS___{}___FINISHED",self.id)
151 }
152}
153
154impl Drop for LogProgressBar {
155 fn drop(&mut self) {
156 if *self.finished.lock().unwrap() {
157 return
158 }
159 log::info!("___PROGRESS___{}___FINISHED",self.id);
160 }
161}
162
163
164#[test]
165fn test_progress_bar() {
166 use mtlog::logger_config;
167 logger_config()
168 .init_global();
169 let pb = LogProgressBar::new(100, "Test");
170 for _ in 0..50 {
171 pb.inc(1);
172 }
173 pb.finish();
174 std::thread::sleep(std::time::Duration::from_millis(1));
175}