#[cfg(feature = "smarter_progress_bar")]
use std::fmt::Arguments;
use std::{
io::{self, Write},
sync::RwLock,
};
use crate::progress::ProgressRenderer;
fn terminal_width() -> usize {
#[cfg(feature = "smarter_progress_bar")]
{
crossterm::terminal::size().map(|(w, _)| w).unwrap_or(80) as usize
}
#[cfg(not(feature = "smarter_progress_bar"))]
{
80
}
}
pub struct CliProgressRenderer {
bar_width: usize,
suffix: RwLock<String>,
}
impl CliProgressRenderer {
pub fn new(bar_width: usize) -> Self {
Self {
bar_width,
suffix: RwLock::new(String::new()),
}
}
fn draw(&self, value: usize, max: usize, suffix: &str) {
let max = max.max(1);
let value = value.min(max);
let value_width = max.to_string().len();
let prefix = format!("[ {value:>value_width$} / {max} ]");
let bar_width = self.bar_width.saturating_sub(prefix.len() + 3);
let progress = (value as f64 / max as f64 * bar_width as f64).round() as usize;
let has_tip = progress > 0 && value < max;
let body = progress.saturating_sub(has_tip as usize);
let truncated_suffix = {
let suffix_max_width = terminal_width().saturating_sub(self.bar_width + 1);
&suffix[..suffix.len().min(suffix_max_width)]
};
print!(
"\r{prefix} [{body_str}{tip_str}{none_str}] {truncated_suffix}",
body_str = "=".repeat(body),
tip_str = if has_tip { ">" } else { "" },
none_str = "-".repeat(bar_width.saturating_sub(progress)) );
let _ = io::stdout().flush();
}
#[cfg(feature = "smarter_progress_bar")]
pub fn print_above(&self, message: Arguments) {
use crossterm::{cursor::MoveToColumn, execute, terminal::Clear};
let mut stdout = io::stdout();
let _ = execute!(
stdout,
Clear(crossterm::terminal::ClearType::CurrentLine),
MoveToColumn(0)
);
let _ = stdout.write_fmt(message);
let _ = stdout.write("\n".as_bytes());
}
pub fn set_suffix(&self, suffix: impl AsRef<str>) {
let mut m_suffix = self.suffix.write().unwrap();
*m_suffix = suffix.as_ref().to_string()
}
}
impl ProgressRenderer for CliProgressRenderer {
fn on_start(&self, value: usize, max: usize) {
let suffix = self.suffix.read().unwrap();
self.draw(value, max, &suffix);
}
fn on_update(&self, value: usize, max: usize) {
let suffix = self.suffix.read().unwrap();
self.draw(value, max, &suffix);
}
fn on_finish(&self) {
println!()
}
fn on_notify(&self, msg: &str) {
#[cfg(feature = "smarter_progress_bar")]
self.print_above(format_args!("{msg}"));
}
fn set_label(&self, msg: &str) {
self.set_suffix(msg);
}
}