Skip to content

5. Progress and live output

Progress bars

use rich::{Console, Progress, ProgressColumn};

let mut progress = Progress::new().columns(vec![
    ProgressColumn::Description,
    ProgressColumn::Bar,
    ProgressColumn::Percentage,
]);

progress.add_task("Downloading", 100.0, 60.0);   // description, total, completed
progress.add_task("Extracting", 100.0, 30.0);

console.print(&progress);

Progress

Progress renders a snapshot — it draws the bars at the values you give it. To animate, update the values and redraw, which is what Live is for.

Available columns: Description, Bar, Percentage, and Text(String, Style) for a fixed cell.

Spinners

use rich::Spinner;

let spinner = Spinner::new("dots");
let frame = spinner.render(elapsed_seconds);
console.print(&frame);

Spinner

render takes a time in seconds and returns the frame for that moment, so the animation is a pure function of elapsed time — no internal clock, and easy to test.

Live displays

Live redraws a renderable in place, moving the cursor back over its previous output rather than scrolling:

use rich::{Console, Live, Spinner, Text};
use std::time::{Duration, Instant};

let console = Console::new();
let started = Instant::now();
let mut live = Live::new(&console);

for _ in 0..50 {
    let frame = Spinner::new("dots").render(started.elapsed().as_secs_f64());
    live.update(&Text::new("  ").append_text(&frame));
    std::thread::sleep(Duration::from_millis(80));
}
live.finish();

One live display at a time

Live owns the cursor while it runs. Printing to the same console from elsewhere — including from another thread — will interleave with its redraws and corrupt the display.

Status

Status is a Live with a spinner and a message, for the common "working on it" case:

use rich::Status;

let status = Status::new("Fetching…");

Next: The CLI →