Push zpwxxpnpktps #67
@@ -5,18 +5,20 @@ use indicatif::{ProgressBar, ProgressStyle};
|
||||
use tracing::{debug, info};
|
||||
|
||||
const BRAILLE: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
const ETA_REFRESH_MS: u64 = 500;
|
||||
const ETA_MIN_ELAPSED_MS: u64 = 1000;
|
||||
const ETA_CUSTOM_HOLD_MS: u64 = 2000;
|
||||
|
||||
/// Wrapper around `ProgressBar` that emits `tracing` events when stderr is not
|
||||
/// a TTY (e.g. HPC job logs): every 10% for bounded bars, every ~10 s for
|
||||
/// spinners (throttled on `set_message`).
|
||||
pub struct TracedBar {
|
||||
pb: ProgressBar,
|
||||
label: String,
|
||||
unit: String,
|
||||
total: u64, // 0 for spinners
|
||||
start: Instant, // creation time, for spinner throttling
|
||||
last_pct: AtomicU64, // last emitted 10%-bucket (1..=10), 0 = none yet
|
||||
last_log_ms: AtomicU64, // ms since `start` at last spinner log
|
||||
total: u64,
|
||||
start: Instant,
|
||||
last_pct: AtomicU64,
|
||||
last_log_ms: AtomicU64,
|
||||
last_eta_refresh: AtomicU64,
|
||||
last_custom_msg_ms: AtomicU64,
|
||||
}
|
||||
|
||||
impl TracedBar {
|
||||
@@ -40,15 +42,60 @@ impl TracedBar {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.pb.is_hidden() && self.total > 0 {
|
||||
self.maybe_update_eta();
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_update_eta(&self) {
|
||||
let now_ms = self.start.elapsed().as_millis() as u64;
|
||||
|
||||
if now_ms < ETA_MIN_ELAPSED_MS {
|
||||
return;
|
||||
}
|
||||
|
||||
let last = self.last_eta_refresh.load(Ordering::Relaxed);
|
||||
if now_ms < last + ETA_REFRESH_MS {
|
||||
return;
|
||||
}
|
||||
|
||||
let last_custom = self.last_custom_msg_ms.load(Ordering::Relaxed);
|
||||
if now_ms < last_custom + ETA_CUSTOM_HOLD_MS {
|
||||
return;
|
||||
}
|
||||
|
||||
let pos = self.pb.position();
|
||||
let remaining = self.total - pos;
|
||||
|
||||
if pos == 0 || remaining == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let elapsed_secs = self.start.elapsed().as_secs_f64();
|
||||
let avg_rate = pos as f64 / elapsed_secs;
|
||||
let eta_secs = remaining as f64 / avg_rate;
|
||||
|
||||
self.pb.set_message(format!("(eta: {})", fmt_secs(eta_secs)));
|
||||
|
||||
let _ = self.last_eta_refresh.compare_exchange(
|
||||
last,
|
||||
now_ms,
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_message(&self, msg: impl Into<String>) {
|
||||
let msg = msg.into();
|
||||
self.last_custom_msg_ms.store(
|
||||
self.start.elapsed().as_millis() as u64,
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
if self.pb.is_hidden() {
|
||||
if self.total > 0 {
|
||||
debug!(stage = %self.label, "{msg}");
|
||||
} else {
|
||||
// spinner: throttle to ~10 s
|
||||
let now_ms = self.start.elapsed().as_millis() as u64;
|
||||
let last = self.last_log_ms.load(Ordering::Relaxed);
|
||||
if now_ms >= last + 10_000
|
||||
@@ -69,6 +116,18 @@ impl TracedBar {
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_secs(s: f64) -> String {
|
||||
if s >= 3600.0 {
|
||||
format!("{:.0}h", s / 3600.0)
|
||||
} else if s >= 60.0 {
|
||||
format!("{:.0}min", s / 60.0)
|
||||
} else if s >= 1.0 {
|
||||
format!("{:.1}s", s)
|
||||
} else {
|
||||
format!("{:.0}ms", s * 1000.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Spinner with the standard project look: `⠋ label — msg 0s`.
|
||||
/// Caller updates the message with `pb.set_message(...)`.
|
||||
pub fn spinner(label: &str) -> TracedBar {
|
||||
@@ -87,6 +146,8 @@ pub fn spinner(label: &str) -> TracedBar {
|
||||
start: Instant::now(),
|
||||
last_pct: AtomicU64::new(0),
|
||||
last_log_ms: AtomicU64::new(0),
|
||||
last_eta_refresh: AtomicU64::new(0),
|
||||
last_custom_msg_ms: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +157,7 @@ pub fn progress_bar(label: &str, n: u64, unit: &str) -> TracedBar {
|
||||
let pb = ProgressBar::new(n);
|
||||
pb.set_style(
|
||||
ProgressStyle::with_template(&format!(
|
||||
"{{spinner}} {label} — {{bar:40.cyan/blue}} {{pos}}/{{len}} {unit} {{elapsed}} (eta: {{eta}})"
|
||||
"{{spinner}} {label} — {{bar:40.cyan/blue}} {{pos}}/{{len}} {unit} {{elapsed}} {{msg}}"
|
||||
))
|
||||
.unwrap()
|
||||
.tick_strings(BRAILLE),
|
||||
@@ -110,5 +171,7 @@ pub fn progress_bar(label: &str, n: u64, unit: &str) -> TracedBar {
|
||||
start: Instant::now(),
|
||||
last_pct: AtomicU64::new(0),
|
||||
last_log_ms: AtomicU64::new(0),
|
||||
last_eta_refresh: AtomicU64::new(0),
|
||||
last_custom_msg_ms: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user