Files
polyscribe/crates/polyscribe-core/src/ui/progress.rs

140 lines
4.0 KiB
Rust

// SPDX-License-Identifier: MIT
// Copyright (c) 2025 <COPYRIGHT HOLDER>. All rights reserved.
use std::io::IsTerminal as _;
/// Manages a set of per-file progress bars plus a top aggregate bar using cliclack.
pub struct ProgressManager {
enabled: bool,
per: Vec<cliclack::ProgressBar>,
total: Option<cliclack::ProgressBar>,
completed: usize,
total_len: usize,
}
impl ProgressManager {
/// Create a new manager with the given enabled flag.
pub fn new(enabled: bool) -> Self {
Self {
enabled,
per: Vec::new(),
total: None,
completed: 0,
total_len: 0,
}
}
/// Create a manager that enables bars when `n > 1`, stderr is a TTY, and not quiet.
pub fn default_for_files(n: usize) -> Self {
let enabled = n > 1
&& std::io::stderr().is_terminal()
&& !crate::is_quiet()
&& !crate::is_no_progress();
Self::new(enabled)
}
/// Initialize bars for the given file labels. If disabled or single file, no-op.
pub fn init_files(&mut self, labels: &[String]) {
self.total_len = labels.len();
if !self.enabled || labels.len() <= 1 {
// No bars in single-file mode or when disabled
self.enabled = false;
return;
}
// Aggregate bar at the top
let total = cliclack::progress_bar(labels.len() as u64);
total.start("Total");
self.total = Some(total);
// Per-file bars (100% scale for each)
for label in labels {
let pb = cliclack::progress_bar(100);
pb.start(label);
self.per.push(pb);
}
}
/// Returns true when bars are enabled (multi-file TTY mode).
pub fn is_enabled(&self) -> bool {
self.enabled
}
/// Update a per-file bar message.
pub fn set_per_message(&mut self, idx: usize, message: &str) {
if !self.enabled {
return;
}
if let Some(pb) = self.per.get_mut(idx) {
pb.set_message(message);
}
}
/// Update a per-file bar percent (0..=100).
pub fn set_per_percent(&mut self, idx: usize, percent: u64) {
if !self.enabled {
return;
}
if let Some(pb) = self.per.get_mut(idx) {
let p = percent.min(100);
pb.set_message(format!("{p}%"));
}
}
/// Mark a file as finished (set to 100% and update total counter).
pub fn mark_file_done(&mut self, idx: usize) {
if !self.enabled {
return;
}
if let Some(pb) = self.per.get_mut(idx) {
pb.stop("done");
}
self.completed += 1;
if let Some(total) = &mut self.total {
total.inc(1);
if self.completed >= self.total_len {
total.stop("all done");
}
}
}
/// Finish the aggregate bar with a custom message.
pub fn finish_total(&mut self, message: &str) {
if !self.enabled {
return;
}
if let Some(total) = &mut self.total {
total.stop(message);
}
}
}
/// A simple reporter for displaying progress messages using cliclack logging.
#[derive(Debug)]
pub struct ProgressReporter {
non_interactive: bool,
}
impl ProgressReporter {
/// Creates a new progress reporter.
pub fn new(non_interactive: bool) -> Self {
Self { non_interactive }
}
/// Displays a progress step message.
pub fn step(&mut self, message: &str) {
if self.non_interactive {
let _ = cliclack::log::info(format!("[..] {message}"));
} else {
let _ = cliclack::log::info(format!("{message}"));
}
}
/// Displays a completion message.
pub fn finish_with_message(&mut self, message: &str) {
if self.non_interactive {
let _ = cliclack::log::info(format!("[ok] {message}"));
} else {
let _ = cliclack::log::info(format!("{message}"));
}
}
}