Skip to main content

contributor_graphs/
progress.rs

1//! Progress bars for the slow, countable phases — cloning many repositories
2//! and the parallel GitHub enrichment passes — built on `indicatif`. The
3//! styling mirrors Seqera's RustQC (cyan braille spinner, a filled bar, and a
4//! dim elapsed time) so the two tools feel consistent.
5//!
6//! Bars draw to stderr. `indicatif` hides them automatically when stderr is not
7//! a terminal, so piped and CI output stays clean; callers additionally pass
8//! `show` (the verbose flag) to opt out entirely. When hidden, the returned bar
9//! is a no-op, so `inc`/`finish_and_clear` are safe to call unconditionally.
10
11use indicatif::{ProgressBar, ProgressStyle};
12use std::time::Duration;
13
14/// Braille spinner frames, matching RustQC's progress style.
15const TICKS: &[&str] = &["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", "⣾"];
16
17/// A determinate progress bar over `total` items, rendered as
18/// `⣾ label [████░░░░] 12/57 3s`. Returns a hidden (no-op) bar when `show` is
19/// false or there is nothing to count, so callers don't need to branch.
20pub fn bar(label: &str, total: usize, show: bool) -> ProgressBar {
21    if !show || total == 0 {
22        return ProgressBar::hidden();
23    }
24    let pb = ProgressBar::new(total as u64);
25    pb.set_style(
26        ProgressStyle::with_template(
27            "  {spinner:.cyan} {msg} [{bar:24.cyan/dim}] {pos}/{len} {elapsed:.dim}",
28        )
29        .expect("valid progress template")
30        .progress_chars("█▉▊▋▌▍▎▏ ")
31        .tick_strings(TICKS),
32    );
33    pb.set_message(label.to_string());
34    // Animate the spinner even while a single long item (e.g. a big clone) is in
35    // flight and the count isn't moving.
36    pb.enable_steady_tick(Duration::from_millis(100));
37    pb
38}