strides #
A command-line UI library to enhance async programs with progress bars and spinners.
The crate is built around two extension traits and two containers:
FutureExtadds.progress(theme)(sugar for.progressive().with_theme(theme)),.progressive()(unconfigured; inherits the parent Group's theme or renders withTheme::default()if awaited directly), and the.with_label(...)/.with_messages(...)/.with_progress(...)/.with_elapsed_time()setters that implicitly lift a bareFutureinto aProgressFuture.StreamExtadds.progress(theme, ...)/.progress_bytes(theme, ...)(sugar) and.progressive()/.progressive_bytes()(unconfigured) to anyStream.future::Groupruns manyFutures concurrently, rendering one line per task.stream::Groupruns manyStreams concurrently, rendering one line per stream.- For byte-counted file copies and downloads, convert an
AsyncReadto a byte stream (e.g. viatokio_util::io::ReaderStream) and use.progress_bytes(...).
Each .progress(...) call animates automatically and returns a builder for
further customization. A Theme bundles a Spinner, a Bar and a
Layout, and is accepted everywhere a theme is expected. A Layout is an
ordered list of Segments controlling the order, spacing and formatting of
each progress line. See spinner::styles and bar::styles for predefined
variants.
Pick a mode by how many tasks you have and how many terminal rows you want to spend on them:
| Tasks | Rows | Futures | Streams |
|---|---|---|---|
| 1 | 1 | fut.progress(theme).await |
s.progress(theme, ...) / s.progress_bytes(theme, ...) |
| N | N | Group::new(theme) + group.push(fut) per task |
stream::Group::new(theme) + group.push(s.progressive(...)) per stream |
| N | 1 | join(futs).with_theme(theme).await |
n/a |
| N | 1-of-many | group.push(join(futs).with_label(...)) |
n/a |
The last row uses future::join inside a future::Group: many futures
collapse into a single progress line that sits alongside other rows in the
Group. Streams do not have a join collapse, s— push each stream as its own
row.
Example #
This example demonstrates how to animate single futures, a group of futures and
a stream. Run it with cargo run --example readme.
use std::time::Duration;
use async_io::Timer;
use futures_lite::StreamExt;
use strides::spinner::styles::{DOTS_3, SAND};
async fn animate_simple_future() {
use strides::future::FutureExt as _;
Timer::after(Duration::from_secs(2))
.progress(DOTS_3)
.with_label("some work going on for two seconds")
.await;
}
async fn animate_two_futures_concurrently() {
use strides::future::{FutureExt as _, Group};
let mut group = Group::new(SAND).with_elapsed_time();
group.push(Timer::after(Duration::from_secs(2)).with_label("two seconds"));
group.push(Timer::after(Duration::from_secs(3)).with_label("three seconds"));
group.for_each(|_| {}).await;
}
async fn animate_stream() {
use strides::stream::StreamExt as _;
let theme = strides::Theme::default()
.with_spinner(DOTS_3)
.with_bar(strides::bar::styles::THIN_LINE);
futures_lite::stream::iter(0..100)
.progress(theme, |_, item| *item as f64 / 100.0)
.with_label("streaming 100 items")
.then(|item| async move {
Timer::after(Duration::from_millis(20)).await;
item
})
.for_each(|_| {})
.await;
}
fn main() {
futures_lite::future::block_on(async {
animate_simple_future().await;
animate_two_futures_concurrently().await;
animate_stream().await;
})
}
See the examples directory for more elaborate uses including downloads, dynamic messages, per-task progress bars, and custom layouts.
Minimum supported Rust version #
strides requires Rust 1.85 or newer.