use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
pub use frames::*;
use crate::iterators::balanced_single;
use crate::style::StyledString;
use crate::terminal::console_render::{get_rows, push_content_up};
use crate::terminal::{console_cursor, console_render};
pub mod frames;
pub struct ProgressBar {
bar: Arc<Mutex<HashMap<usize, Frames>>>,
clear_type: Arc<Mutex<Option<u16>>>,
stop: Arc<Mutex<bool>>,
}
impl Default for ProgressBar {
fn default() -> Self {
let progress = Self::new(Frames::default());
progress.run_all();
progress
}
}
impl ProgressBar {
pub fn new(bar: Frames) -> Self {
console_cursor::save_hide_cursor();
let progress = ProgressBar {
bar: Arc::new(Mutex::new(HashMap::new())),
stop: Arc::new(Mutex::new(false)),
clear_type: Arc::new(Mutex::new(Some(get_rows()))),
};
progress.add(bar);
progress
}
pub fn add(&self, bar: Frames) -> usize {
let mut bar_map = self.bar.lock().unwrap();
let uid: usize = bar_map.len() + 1_usize;
bar_map.insert(uid, bar);
uid
}
pub fn clear(&self, rows: Option<u16>) {
*self.clear_type.lock().unwrap() = rows;
}
pub fn set(&self, uid: &usize, new_current: &usize) {
if let Some(bar) = self.bar.lock().unwrap().get_mut(uid) {
let current = bar.current;
let diff = new_current.saturating_sub(current);
bar.inc(&diff);
}
}
pub fn get(&self, uid: &usize) -> Option<usize> {
if let Some(bar) = self.bar.lock().unwrap().get(uid) {
let current = bar.current;
Some(current)
} else {
None
}
}
pub fn run_all(&self) {
if let Some(rows) = *self.clear_type.lock().unwrap() {
push_content_up(rows);
}
let bars = Arc::clone(&self.bar);
let stop = Arc::clone(&self.stop);
let mut last_render_time = Instant::now();
thread::spawn(move || {
let mut frame_index = 0_usize;
while !*stop.lock().unwrap() {
for (index, frames) in bars.lock().unwrap().iter() {
let mut line = Vec::new();
let size: usize = frames.size;
let goal = frames.goal;
let current: usize = frames.current;
let completion_percentage = (current as f64 / goal as f64) * 100.0;
let complete_size = ((completion_percentage / 100.0) * size as f64) as usize;
let incomplete_size = size - complete_size;
if Instant::now() - last_render_time >= Duration::from_millis(100) {
frame_index += 1; last_render_time = Instant::now(); }
line.push(balanced_single(frame_index, &frames.begin.clone()).clone());
line.push(
balanced_single(frame_index, &frames.bar_complete_char.clone())
.repeat(complete_size),
);
line.push(balanced_single(frame_index, &frames.limiter.clone()).clone());
line.push(
balanced_single(frame_index, &frames.bar_incomplete_char.clone())
.repeat(incomplete_size),
);
line.push(balanced_single(frame_index, &frames.end.clone()).clone());
line.push(StyledString::new(&format!(" {:.2}", completion_percentage)));
line.push(StyledString::new("%"));
line.push(StyledString::new(" | "));
line.push(StyledString::new(&format!("{}", current)));
line.push(StyledString::new("/"));
line.push(StyledString::new(&format!("{}", goal)));
console_render::render_styled_line(*index as u16, &line);
}
}
});
}
pub fn get_last(&self) -> usize {
let bar_map = self.bar.lock().unwrap();
bar_map.len()
}
}
impl Drop for ProgressBar {
fn drop(&mut self) {
*self.stop.lock().unwrap() = true;
console_cursor::reset_cursor();
console_cursor::next_line(self.bar.lock().unwrap().len() as u16);
}
}