indicatif-0.16.0/.cargo_vcs_info.json0000644000000001120000000000000130430ustar { "git": { "sha1": "73a0be3e7c9b0706856fd463ee8c478be58c1556" } } indicatif-0.16.0/.github/workflows/rust.yml000064400000000000000000000027330000000000000167410ustar 00000000000000name: CI on: push: branches: ['main'] pull_request: jobs: test: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] rust: [stable, beta] exclude: - os: macos-latest rust: beta - os: windows-latest rust: beta runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - uses: actions-rs/cargo@v1 with: command: build args: --workspace --all-targets - uses: actions-rs/cargo@v1 with: command: test args: --workspace - uses: actions-rs/cargo@v1 with: command: test args: --workspace --all-features lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true components: rustfmt, clippy - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - uses: actions-rs/cargo@v1 if: always() with: command: clippy args: --workspace --all-targets -- -D warnings audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: EmbarkStudios/cargo-deny-action@v1 indicatif-0.16.0/.gitignore000064400000000000000000000000220000000000000136010ustar 00000000000000target Cargo.lock indicatif-0.16.0/Cargo.lock0000644000000201070000000000000110240ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "console" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" dependencies = [ "encode_unicode", "lazy_static", "libc", "regex", "terminal_size", "unicode-width", "winapi", ] [[package]] name = "crossbeam-channel" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" dependencies = [ "cfg-if", "crossbeam-utils", "lazy_static", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" dependencies = [ "autocfg", "cfg-if", "lazy_static", ] [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "getrandom" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hermit-abi" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] [[package]] name = "indicatif" version = "0.16.0" dependencies = [ "console", "lazy_static", "number_prefix", "rand", "rayon", "regex", "tokio", "unicode-segmentation", "unicode-width", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" [[package]] name = "memoffset" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "pin-project-lite" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" [[package]] name = "ppv-lite86" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "rand" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", "rand_chacha", "rand_core", "rand_hc", ] [[package]] name = "rand_chacha" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ "rand_core", ] [[package]] name = "rayon" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" dependencies = [ "autocfg", "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", "lazy_static", "num_cpus", ] [[package]] name = "regex" version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "terminal_size" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" dependencies = [ "libc", "winapi", ] [[package]] name = "tokio" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5" dependencies = [ "autocfg", "pin-project-lite", ] [[package]] name = "unicode-segmentation" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" indicatif-0.16.0/Cargo.toml0000644000000031440000000000000110510ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "indicatif" version = "0.16.0" authors = ["Armin Ronacher ", "Dirkjan Ochtman "] exclude = ["screenshots/*"] description = "A progress bar and cli reporting library for Rust" documentation = "https://docs.rs/indicatif" readme = "README.md" keywords = ["cli", "progress", "pb", "colors", "progressbar"] license = "MIT" repository = "https://github.com/mitsuhiko/indicatif" [dependencies.console] version = ">=0.9.1, <1.0.0" [dependencies.lazy_static] version = "1.0" [dependencies.number_prefix] version = "0.4" [dependencies.rayon] version = "1.0" optional = true [dependencies.regex] version = "1.3.1" features = ["std"] default-features = false [dependencies.unicode-segmentation] version = "1.6.0" optional = true [dependencies.unicode-width] version = "0.1.7" optional = true [dev-dependencies.rand] version = "0.8" [dev-dependencies.tokio] version = "1.0" features = ["time", "rt"] [features] default = [] improved_unicode = ["unicode-segmentation", "unicode-width", "console/unicode-width"] with_rayon = ["rayon"] indicatif-0.16.0/Cargo.toml.orig000064400000000000000000000020050000000000000145030ustar 00000000000000[package] name = "indicatif" description = "A progress bar and cli reporting library for Rust" version = "0.16.0" keywords = ["cli", "progress", "pb", "colors", "progressbar"] authors = ["Armin Ronacher ", "Dirkjan Ochtman "] license = "MIT" repository = "https://github.com/mitsuhiko/indicatif" documentation = "https://docs.rs/indicatif" readme = "README.md" edition = "2018" exclude = ["screenshots/*"] [dependencies] regex = { version = "1.3.1", default-features = false, features = ["std"] } lazy_static = "1.0" number_prefix = "0.4" console = ">=0.9.1, <1.0.0" unicode-segmentation = { version = "1.6.0", optional = true } unicode-width = { version = "0.1.7", optional = true } rayon = { version = "1.0", optional = true } [dev-dependencies] rand = "0.8" tokio = { version = "1.0", features = ["time", "rt"] } [features] default = [] improved_unicode = ["unicode-segmentation", "unicode-width", "console/unicode-width"] # Legacy alias for `rayon` with_rayon = ["rayon"] indicatif-0.16.0/LICENSE000064400000000000000000000021300000000000000126200ustar 00000000000000The MIT License (MIT) Copyright (c) 2017 Armin Ronacher Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. indicatif-0.16.0/README.md000064400000000000000000000020500000000000000130730ustar 00000000000000# indicatif [![build status](https://travis-ci.org/mitsuhiko/indicatif.svg?branch=main)](https://travis-ci.org/mitsuhiko/indicatif) [![Crates.io](https://img.shields.io/crates/v/indicatif.svg)](https://crates.io/crates/indicatif) [Documentation](https://docs.rs/indicatif) A Rust library for indicating progress in command line applications to users. This currently primarily provides progress bars and spinners as well as basic color support, but there are bigger plans for the future of this! ## Examples [examples/yarnish.rs](examples/yarnish.rs) [examples/download.rs](examples/download.rs) [examples/multi.rs](examples/multi.rs) [examples/single.rs](examples/single.rs) indicatif-0.16.0/deny.toml000064400000000000000000000000730000000000000134530ustar 00000000000000[licenses] allow-osi-fsf-free = "either" copyleft = "deny" indicatif-0.16.0/examples/cargo.rs000064400000000000000000000072600000000000000151030ustar 00000000000000use std::sync::{mpsc, Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; use console::{Style, Term}; use indicatif::{HumanDuration, ProgressBar, ProgressStyle}; use rand::Rng; static CRATES: &[(&str, &str)] = &[ ("console", "v0.14.1"), ("lazy_static", "v1.4.0"), ("libc", "v0.2.93"), ("regex", "v1.4.6"), ("regex-syntax", "v0.6.23"), ("terminal_size", "v0.1.16"), ("libc", "v0.2.93"), ("unicode-width", "v0.1.8"), ("lazy_static", "v1.4.0"), ("number_prefix", "v0.4.0"), ("regex", "v1.4.6"), ("rand", "v0.8.3"), ("getrandom", "v0.2.2"), ("cfg-if", "v1.0.0"), ("libc", "v0.2.93"), ("rand_chacha", "v0.3.0"), ("ppv-lite86", "v0.2.10"), ("rand_core", "v0.6.2"), ("getrandom", "v0.2.2"), ("rand_core", "v0.6.2"), ("tokio", "v1.5.0"), ("bytes", "v1.0.1"), ("pin-project-lite", "v0.2.6"), ("slab", "v0.4.3"), ("indicatif", "v0.15.0"), ]; fn main() { // number of cpus const NUM_CPUS: usize = 4; let start = Instant::now(); // mimic cargo progress bar although it behaves a bit different let pb = ProgressBar::new(CRATES.len() as u64); pb.set_style( ProgressStyle::default_bar() // note that bar size is fixed unlike cargo which is dynamic // and also the truncation in cargo uses trailers (`...`) .template(if Term::stdout().size().1 > 80 { "{prefix:>12.cyan.bold} [{bar:57}] {pos}/{len} {wide_msg}" } else { "{prefix:>12.cyan.bold} [{bar:57}] {pos}/{len}" }) .progress_chars("=> "), ); pb.set_prefix("Building"); // process in another thread // crates to be iterated but not exactly a tree let crates = Arc::new(Mutex::new(CRATES.iter())); let (tx, rx) = mpsc::channel(); for n in 0..NUM_CPUS { let tx = tx.clone(); let crates = crates.clone(); thread::spawn(move || { let mut rng = rand::thread_rng(); loop { let krate = crates.lock().unwrap().next(); // notify main thread if n thread is processing a crate tx.send((n, krate)).unwrap(); if let Some(krate) = krate { thread::sleep(Duration::from_millis( // last compile and linking is always slow, let's mimic that if CRATES.last() == Some(krate) { rng.gen_range(1_000..2_000) } else { rng.gen_range(250..1_000) }, )); } else { break; } } }); } // drop tx to stop waiting drop(tx); let green_bold = Style::new().green().bold(); // do progress drawing in main thread let mut processing = vec![None; NUM_CPUS]; while let Ok((n, krate)) = rx.recv() { processing[n] = krate; let crates: Vec<&str> = processing .iter() .filter_map(|t| t.copied().map(|(name, _)| name)) .collect(); pb.set_message(crates.join(", ")); if let Some((name, version)) = krate { // crate is being built let line = format!( "{:>12} {} {}", green_bold.apply_to("Compiling"), name, version ); pb.println(line); pb.inc(1); } } pb.finish_and_clear(); // compilation is finished println!( "{:>12} dev [unoptimized + debuginfo] target(s) in {}", green_bold.apply_to("Finished"), HumanDuration(start.elapsed()) ); } indicatif-0.16.0/examples/cargowrap.rs000064400000000000000000000020230000000000000157650ustar 00000000000000use std::io::{BufRead, BufReader}; use std::process; use std::time::Instant; use indicatif::{HumanDuration, ProgressBar, ProgressStyle}; pub fn main() { let started = Instant::now(); println!("Compiling package in release mode..."); let pb = ProgressBar::new_spinner(); pb.enable_steady_tick(200); pb.set_style( ProgressStyle::default_spinner() .tick_chars("/|\\- ") .template("{spinner:.dim.bold} cargo: {wide_msg}"), ); let mut p = process::Command::new("cargo") .arg("build") .arg("--release") .stderr(process::Stdio::piped()) .spawn() .unwrap(); for line in BufReader::new(p.stderr.take().unwrap()).lines() { let line = line.unwrap(); let stripped_line = line.trim(); if !stripped_line.is_empty() { pb.set_message(stripped_line.to_owned()); } pb.tick(); } p.wait().unwrap(); pb.finish_and_clear(); println!("Done in {}", HumanDuration(started.elapsed())); } indicatif-0.16.0/examples/download-continued.rs000064400000000000000000000013100000000000000175730ustar 00000000000000use std::cmp::min; use std::thread; use std::time::Duration; use indicatif::{ProgressBar, ProgressStyle}; fn main() { let mut downloaded = 69369369; let total_size = 231231231; let pb = ProgressBar::new(total_size); pb.set_style( ProgressStyle::default_bar() .template("{spinner:.green} [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})") .progress_chars("#>-"), ); pb.set_position(downloaded); pb.reset_eta(); while downloaded < total_size { downloaded = min(downloaded + 123211, total_size); pb.set_position(downloaded); thread::sleep(Duration::from_millis(12)); } pb.finish_with_message("downloaded"); } indicatif-0.16.0/examples/download-speed.rs000064400000000000000000000012520000000000000167100ustar 00000000000000use std::cmp::min; use std::thread; use std::time::Duration; use indicatif::{ProgressBar, ProgressStyle}; fn main() { let mut downloaded = 0; let total_size = 231231231; let pb = ProgressBar::new(total_size); pb.set_style(ProgressStyle::default_bar() .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})") .progress_chars("#>-")); while downloaded < total_size { let new = min(downloaded + 223211, total_size); downloaded = new; pb.set_position(new); thread::sleep(Duration::from_millis(12)); } pb.finish_with_message("downloaded"); } indicatif-0.16.0/examples/download.rs000064400000000000000000000012310000000000000156070ustar 00000000000000use std::cmp::min; use std::thread; use std::time::Duration; use indicatif::{ProgressBar, ProgressStyle}; fn main() { let mut downloaded = 0; let total_size = 231231231; let pb = ProgressBar::new(total_size); pb.set_style(ProgressStyle::default_bar() .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})") .progress_chars("#>-")); while downloaded < total_size { let new = min(downloaded + 223211, total_size); downloaded = new; pb.set_position(new); thread::sleep(Duration::from_millis(12)); } pb.finish_with_message("downloaded"); } indicatif-0.16.0/examples/fastbar.rs000064400000000000000000000023470000000000000154330ustar 00000000000000use std::time::Instant; use indicatif::{HumanDuration, ProgressBar}; fn many_units_of_easy_work(n: u64, label: &str, draw_delta: Option) { let pb = ProgressBar::new(n); if let Some(v) = draw_delta { pb.set_draw_delta(v); } let mut sum = 0; let started = Instant::now(); for i in 0..n { // Any quick computation, followed by an update to the progress bar. sum += 2 * i + 3; pb.inc(1); } pb.finish(); let finished = started.elapsed(); println!( "[{}] Sum ({}) calculated in {}", label, sum, HumanDuration(finished) ); } fn main() { const N: u64 = 1 << 20; // Perform a long sequence of many simple computations monitored by a // default progress bar. many_units_of_easy_work(N, "Default progress bar ", None); // Perform the same sequence of many simple computations, but only redraw // after each 0.005% of additional progress. many_units_of_easy_work(N, "Draw delta is 0.005% ", Some(N / (5 * 100000))); // Perform the same sequence of many simple computations, but only redraw // after each 0.01% of additional progress. many_units_of_easy_work(N, "Draw delta is 0.01% ", Some(N / 10000)); } indicatif-0.16.0/examples/finebars.rs000064400000000000000000000021710000000000000155750ustar 00000000000000use std::thread; use std::time::Duration; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use rand::{thread_rng, Rng}; fn main() { let styles = [ ("Rough bar:", "█ ", "red"), ("Fine bar: ", "█▉▊▋▌▍▎▏ ", "yellow"), ("Vertical: ", "█▇▆▅▄▃▂▁ ", "green"), ("Fade in: ", "█▓▒░ ", "blue"), ("Blocky: ", "█▛▌▖ ", "magenta"), ]; let m = MultiProgress::new(); for s in styles.iter() { let pb = m.add(ProgressBar::new(512)); pb.set_style( ProgressStyle::default_bar() .template(&format!("{{prefix:.bold}}▕{{bar:.{}}}▏{{msg}}", s.2)) .progress_chars(s.1), ); pb.set_prefix(s.0); let wait = Duration::from_millis(thread_rng().gen_range(10..30)); thread::spawn(move || { for i in 0..512 { pb.inc(1); pb.set_message(format!("{:3}%", 100 * i / 512)); thread::sleep(wait); } pb.finish_with_message("100%"); }); } m.join().unwrap(); } indicatif-0.16.0/examples/iterator.rs000064400000000000000000000011670000000000000156410ustar 00000000000000use indicatif::{ProgressBar, ProgressIterator, ProgressStyle}; fn main() { // Default styling, attempt to use Iterator::size_hint to count input size for _ in (0..1000).progress() { // ... } // Provide explicit number of elements in iterator for _ in (0..1000).progress_count(1000) { // ... } // Provide a custom bar style let pb = ProgressBar::new(1000); pb.set_style(ProgressStyle::default_bar().template( "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] ({pos}/{len}, ETA {eta})", )); for _ in (0..1000).progress_with(pb) { // ... } } indicatif-0.16.0/examples/log.rs000064400000000000000000000004630000000000000145670ustar 00000000000000use std::thread; use std::time::Duration; use indicatif::ProgressBar; fn main() { let pb = ProgressBar::new(100); for i in 0..100 { thread::sleep(Duration::from_millis(25)); pb.println(format!("[+] finished #{}", i)); pb.inc(1); } pb.finish_with_message("done"); } indicatif-0.16.0/examples/long-spinner.rs000064400000000000000000000015230000000000000164170ustar 00000000000000use std::thread; use std::time::Duration; use indicatif::{ProgressBar, ProgressStyle}; fn main() { let pb = ProgressBar::new_spinner(); pb.enable_steady_tick(120); pb.set_style( ProgressStyle::default_spinner() // For more spinners check out the cli-spinners project: // https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json .tick_strings(&[ "▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸", "▪▪▪▪▪", ]) .template("{spinner:.blue} {msg}"), ); pb.set_message("Calculating..."); thread::sleep(Duration::from_secs(5)); pb.finish_with_message("Done"); } indicatif-0.16.0/examples/morebars.rs000064400000000000000000000015710000000000000156210ustar 00000000000000use std::sync::Arc; use std::thread; use std::time::Duration; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; fn main() { let m = Arc::new(MultiProgress::new()); let sty = ProgressStyle::default_bar().template("{bar:40.green/yellow} {pos:>7}/{len:7}"); let pb = m.add(ProgressBar::new(5)); pb.set_style(sty.clone()); let m2 = m.clone(); let _ = thread::spawn(move || { // make sure we show up at all. otherwise no rendering // event. pb.tick(); for _ in 0..5 { let pb2 = m2.add(ProgressBar::new(128)); pb2.set_style(sty.clone()); for _ in 0..128 { pb2.inc(1); thread::sleep(Duration::from_millis(5)); } pb2.finish(); pb.inc(1); } pb.finish_with_message("done"); }); m.join().unwrap(); } indicatif-0.16.0/examples/multi-tree-ext.rs000064400000000000000000000202610000000000000166710ustar 00000000000000use console::style; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use lazy_static::lazy_static; use rand::{rngs::ThreadRng, Rng, RngCore}; use std::fmt::Debug; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; use std::time::Duration; #[derive(Debug, Clone)] enum Action { ModifyTree(usize), IncProgressBar(usize), Stop, } #[derive(Clone, Debug)] enum Elem { AddItem(Item), RemoveItem(Index), } #[derive(Clone, Debug)] struct Item { key: String, index: usize, indent: usize, progress_bar: ProgressBar, } #[derive(Clone, Debug)] struct Index(usize); const PB_LEN: u64 = 32; static ELEM_IDX: AtomicUsize = AtomicUsize::new(0); lazy_static! { static ref ELEMENTS: [Elem; 27] = [ Elem::AddItem(Item { indent: 9, index: 0, progress_bar: ProgressBar::new(PB_LEN), key: "dog".to_string() }), Elem::AddItem(Item { indent: 0, index: 0, progress_bar: ProgressBar::new(PB_LEN), key: "temp_1".to_string() }), Elem::AddItem(Item { indent: 8, index: 1, progress_bar: ProgressBar::new(PB_LEN), key: "lazy".to_string() }), Elem::AddItem(Item { indent: 0, index: 1, progress_bar: ProgressBar::new(PB_LEN), key: "temp_2".to_string() }), Elem::AddItem(Item { indent: 1, index: 0, progress_bar: ProgressBar::new(PB_LEN), key: "the".to_string() }), Elem::AddItem(Item { indent: 0, index: 0, progress_bar: ProgressBar::new(PB_LEN), key: "temp_3".to_string() }), Elem::AddItem(Item { indent: 7, index: 3, progress_bar: ProgressBar::new(PB_LEN), key: "a".to_string() }), Elem::AddItem(Item { indent: 0, index: 3, progress_bar: ProgressBar::new(PB_LEN), key: "temp_4".to_string() }), Elem::AddItem(Item { indent: 6, index: 2, progress_bar: ProgressBar::new(PB_LEN), key: "over".to_string() }), Elem::RemoveItem(Index(6)), Elem::RemoveItem(Index(4)), Elem::RemoveItem(Index(3)), Elem::RemoveItem(Index(0)), Elem::AddItem(Item { indent: 0, index: 2, progress_bar: ProgressBar::new(PB_LEN), key: "temp_5".to_string() }), Elem::AddItem(Item { indent: 4, index: 1, progress_bar: ProgressBar::new(PB_LEN), key: "fox".to_string() }), Elem::AddItem(Item { indent: 0, index: 1, progress_bar: ProgressBar::new(PB_LEN), key: "temp_6".to_string() }), Elem::AddItem(Item { indent: 2, index: 1, progress_bar: ProgressBar::new(PB_LEN), key: "quick".to_string() }), Elem::AddItem(Item { indent: 0, index: 1, progress_bar: ProgressBar::new(PB_LEN), key: "temp_7".to_string() }), Elem::AddItem(Item { indent: 5, index: 5, progress_bar: ProgressBar::new(PB_LEN), key: "jumps".to_string() }), Elem::AddItem(Item { indent: 0, index: 5, progress_bar: ProgressBar::new(PB_LEN), key: "temp_8".to_string() }), Elem::AddItem(Item { indent: 3, index: 4, progress_bar: ProgressBar::new(PB_LEN), key: "brown".to_string() }), Elem::AddItem(Item { indent: 0, index: 3, progress_bar: ProgressBar::new(PB_LEN), key: "temp_9".to_string() }), Elem::RemoveItem(Index(10)), Elem::RemoveItem(Index(7)), Elem::RemoveItem(Index(4)), Elem::RemoveItem(Index(3)), Elem::RemoveItem(Index(1)), ]; } /// The example demonstrates the usage of `MultiProgress` and further extends `multi-tree` example. /// Now the example has 3 different actions implemented, and the item tree can be modified /// by inserting or removing progress bars. The progress bars to be removed eventually /// have messages with pattern `"temp_*"`. pub fn main() { let mp = Arc::new(MultiProgress::new()); let sty_main = ProgressStyle::default_bar().template("{bar:40.green/yellow} {pos:>4}/{len:4}"); let sty_aux = ProgressStyle::default_bar().template("[{pos:>2}/{len:2}] {prefix}{spinner:.green} {msg}"); let sty_fin = ProgressStyle::default_bar().template("[{pos:>2}/{len:2}] {prefix}{msg}"); let pb_main = mp.add(ProgressBar::new( ELEMENTS .iter() .map(|e| match e { Elem::AddItem(item) => item.progress_bar.length(), Elem::RemoveItem(_) => 1, }) .sum(), )); pb_main.set_style(sty_main); for e in ELEMENTS.iter() { match e { Elem::AddItem(item) => item.progress_bar.set_style(sty_aux.clone()), Elem::RemoveItem(_) => {} } } let mut items: Vec<&Item> = Vec::with_capacity(ELEMENTS.len()); let mp2 = Arc::clone(&mp); let _ = thread::spawn(move || { let mut rng = ThreadRng::default(); pb_main.tick(); loop { match get_action(&mut rng, &items) { Action::Stop => { // all elements were exhausted pb_main.finish(); return; } Action::ModifyTree(elem_idx) => match &ELEMENTS[elem_idx] { Elem::AddItem(item) => { let pb = mp2.insert(item.index + 1, item.progress_bar.clone()); pb.set_prefix(" ".repeat(item.indent)); pb.set_message(&item.key); items.insert(item.index, &item); } Elem::RemoveItem(Index(index)) => { let item = items.remove(*index); let pb = &item.progress_bar; mp2.remove(pb); pb_main.inc(pb.length() - pb.position()); } }, Action::IncProgressBar(item_idx) => { let item = &items[item_idx]; item.progress_bar.inc(1); let pos = item.progress_bar.position(); let len = item.progress_bar.length(); if pos >= len { item.progress_bar.set_style(sty_fin.clone()); item.progress_bar.finish_with_message(format!( "{} {}", style("✔").green(), item.key )); } pb_main.inc(1); } } thread::sleep(Duration::from_millis(20)); } }); mp.join().unwrap(); } /// The function guarantees to return the action, that is valid for the current tree. fn get_action<'a>(rng: &'a mut dyn RngCore, items: &[&Item]) -> Action { let elem_idx = ELEM_IDX.load(Ordering::SeqCst); // the indices of those items, that not completed yet let uncompleted = items .iter() .enumerate() .filter(|(_, item)| { let pos = item.progress_bar.position(); let len = item.progress_bar.length(); pos < len }) .map(|(idx, _)| idx) .collect::>(); let k = rng.gen_range(0..16); if (k > 0 || k == 0 && elem_idx == ELEMENTS.len()) && !uncompleted.is_empty() { let idx = rng.gen_range(0..uncompleted.len() as u64) as usize; Action::IncProgressBar(uncompleted[idx]) } else if elem_idx < ELEMENTS.len() { ELEM_IDX.fetch_add(1, Ordering::SeqCst); Action::ModifyTree(elem_idx) } else { // nothing to do more Action::Stop } } indicatif-0.16.0/examples/multi-tree.rs000064400000000000000000000134750000000000000161040ustar 00000000000000use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use lazy_static::lazy_static; use rand::{rngs::ThreadRng, Rng, RngCore}; use std::fmt::Debug; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; #[derive(Debug, Clone)] enum Action { AddProgressBar(usize), IncProgressBar(usize), } #[derive(Clone, Debug)] struct Elem { key: String, index: usize, indent: usize, progress_bar: ProgressBar, } lazy_static! { static ref ELEMENTS: [Elem; 9] = [ Elem { indent: 1, index: 0, progress_bar: ProgressBar::new(32), key: "jumps".to_string() }, Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "lazy".to_string() }, Elem { indent: 0, index: 0, progress_bar: ProgressBar::new(32), key: "the".to_string() }, Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "dog".to_string() }, Elem { indent: 2, index: 2, progress_bar: ProgressBar::new(32), key: "over".to_string() }, Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "brown".to_string() }, Elem { indent: 1, index: 1, progress_bar: ProgressBar::new(32), key: "quick".to_string() }, Elem { indent: 3, index: 5, progress_bar: ProgressBar::new(32), key: "a".to_string() }, Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "fox".to_string() }, ]; } /// The example implements the tree-like collection of progress bars, where elements are /// added on the fly and progress bars get incremented until all elements is added and /// all progress bars finished. /// On each iteration `get_action` function returns some action, and when the tree gets /// complete, the function returns `None`, which finishes the loop. fn main() { let mp = Arc::new(MultiProgress::new()); let sty_main = ProgressStyle::default_bar().template("{bar:40.green/yellow} {pos:>4}/{len:4}"); let sty_aux = ProgressStyle::default_bar().template("{spinner:.green} {msg} {pos:>4}/{len:4}"); let pb_main = mp.add(ProgressBar::new( ELEMENTS.iter().map(|e| e.progress_bar.length()).sum(), )); pb_main.set_style(sty_main); for elem in ELEMENTS.iter() { elem.progress_bar.set_style(sty_aux.clone()); } let tree: Arc>> = Arc::new(Mutex::new(Vec::with_capacity(ELEMENTS.len()))); let tree2 = Arc::clone(&tree); let mp2 = Arc::clone(&mp); let _ = thread::spawn(move || { let mut rng = ThreadRng::default(); pb_main.tick(); loop { match get_action(&mut rng, &tree) { None => { // all elements were exhausted pb_main.finish(); return; } Some(Action::AddProgressBar(el_idx)) => { let elem = &ELEMENTS[el_idx]; let pb = mp2.insert(elem.index + 1, elem.progress_bar.clone()); pb.set_message(format!("{} {}", " ".repeat(elem.indent), elem.key)); tree.lock().unwrap().insert(elem.index, &elem); } Some(Action::IncProgressBar(el_idx)) => { let elem = &tree.lock().unwrap()[el_idx]; elem.progress_bar.inc(1); let pos = elem.progress_bar.position(); let len = elem.progress_bar.length(); if pos >= len { elem.progress_bar.finish_with_message(format!( "{}{} {}", " ".repeat(elem.indent), "✔", elem.key )); } pb_main.inc(1); } } thread::sleep(Duration::from_millis(15)); } }); mp.join().unwrap(); println!("==============================="); println!("the tree should be the same as:"); for elem in tree2.lock().unwrap().iter() { println!("{} {}", " ".repeat(elem.indent), elem.key); } } /// The function guarantees to return the action, that is valid for the current tree. fn get_action<'a>(rng: &'a mut dyn RngCore, tree: &Mutex>) -> Option { let elem_len = ELEMENTS.len() as u64; let list_len = tree.lock().unwrap().len() as u64; let sum_free = tree .lock() .unwrap() .iter() .map(|e| { let pos = e.progress_bar.position(); let len = e.progress_bar.length(); len - pos }) .sum::(); if sum_free == 0 && list_len == elem_len { // nothing to do more None } else if sum_free == 0 && list_len < elem_len { // there is no place to make an increment Some(Action::AddProgressBar(tree.lock().unwrap().len())) } else { loop { let list = tree.lock().unwrap(); let k = rng.gen_range(0..17); if k == 0 && list_len < elem_len { return Some(Action::AddProgressBar(list.len())); } else { let l = (k % list_len) as usize; let pos = list[l].progress_bar.position(); let len = list[l].progress_bar.length(); if pos < len { return Some(Action::IncProgressBar(l)); } } } } } indicatif-0.16.0/examples/multi.rs000064400000000000000000000025760000000000000151470ustar 00000000000000use std::thread; use std::time::Duration; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; fn main() { let m = MultiProgress::new(); let sty = ProgressStyle::default_bar() .template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}") .progress_chars("##-"); let pb = m.add(ProgressBar::new(128)); pb.set_style(sty.clone()); let _ = thread::spawn(move || { for i in 0..128 { pb.set_message(format!("item #{}", i + 1)); pb.inc(1); thread::sleep(Duration::from_millis(15)); } pb.finish_with_message("done"); }); let pb = m.add(ProgressBar::new(128)); pb.set_style(sty.clone()); let _ = thread::spawn(move || { for _ in 0..3 { pb.set_position(0); for i in 0..128 { pb.set_message(format!("item #{}", i + 1)); pb.inc(1); thread::sleep(Duration::from_millis(8)); } } pb.finish_with_message("done"); }); let pb = m.add(ProgressBar::new(1024)); pb.set_style(sty); let _ = thread::spawn(move || { for i in 0..1024 { pb.set_message(format!("item #{}", i + 1)); pb.inc(1); thread::sleep(Duration::from_millis(2)); } pb.finish_with_message("done"); }); m.join_and_clear().unwrap(); } indicatif-0.16.0/examples/single.rs000064400000000000000000000004000000000000000152560ustar 00000000000000use std::thread; use std::time::Duration; use indicatif::ProgressBar; fn main() { let pb = ProgressBar::new(1024); for _ in 0..1024 { pb.inc(1); thread::sleep(Duration::from_millis(5)); } pb.finish_with_message("done"); } indicatif-0.16.0/examples/tokio.rs000064400000000000000000000015410000000000000151310ustar 00000000000000use indicatif::ProgressBar; use std::time::Duration; use tokio::runtime; use tokio::time::interval; fn main() { // Plain progress bar, totaling 1024 steps. let steps = 1024; let pb = ProgressBar::new(steps); // Stream of events, triggering every 5ms. let rt = runtime::Builder::new_current_thread() .enable_time() .build() .expect("failed to create runtime"); // Future computation which runs for `steps` interval events, // incrementing one step of the progress bar each time. let future = async { let mut intv = interval(Duration::from_millis(5)); for _ in 0..steps { intv.tick().await; pb.inc(1); } }; // Drive the future to completion, blocking until done. rt.block_on(future); // Mark the progress bar as finished. pb.finish(); } indicatif-0.16.0/examples/yarnish.rs000064400000000000000000000050040000000000000154570ustar 00000000000000use rand::seq::SliceRandom; use rand::Rng; use std::thread; use std::time::{Duration, Instant}; use console::{style, Emoji}; use indicatif::{HumanDuration, MultiProgress, ProgressBar, ProgressStyle}; static PACKAGES: &[&str] = &[ "fs-events", "my-awesome-module", "emoji-speaker", "wrap-ansi", "stream-browserify", "acorn-dynamic-import", ]; static COMMANDS: &[&str] = &[ "cmake .", "make", "make clean", "gcc foo.c -o foo", "gcc bar.c -o bar", "./helper.sh rebuild-cache", "make all-clean", "make test", ]; static LOOKING_GLASS: Emoji<'_, '_> = Emoji("🔍 ", ""); static TRUCK: Emoji<'_, '_> = Emoji("🚚 ", ""); static CLIP: Emoji<'_, '_> = Emoji("🔗 ", ""); static PAPER: Emoji<'_, '_> = Emoji("📃 ", ""); static SPARKLE: Emoji<'_, '_> = Emoji("✨ ", ":-)"); pub fn main() { let mut rng = rand::thread_rng(); let started = Instant::now(); let spinner_style = ProgressStyle::default_spinner() .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ") .template("{prefix:.bold.dim} {spinner} {wide_msg}"); println!( "{} {}Resolving packages...", style("[1/4]").bold().dim(), LOOKING_GLASS ); println!( "{} {}Fetching packages...", style("[2/4]").bold().dim(), TRUCK ); println!( "{} {}Linking dependencies...", style("[3/4]").bold().dim(), CLIP ); let deps = 1232; let pb = ProgressBar::new(deps); for _ in 0..deps { pb.inc(1); thread::sleep(Duration::from_millis(3)); } pb.finish_and_clear(); println!( "{} {}Building fresh packages...", style("[4/4]").bold().dim(), PAPER ); let m = MultiProgress::new(); for i in 0..4 { let count = rng.gen_range(30..80); let pb = m.add(ProgressBar::new(count)); pb.set_style(spinner_style.clone()); pb.set_prefix(format!("[{}/?]", i + 1)); let _ = thread::spawn(move || { let mut rng = rand::thread_rng(); let pkg = PACKAGES.choose(&mut rng).unwrap(); for _ in 0..count { let cmd = COMMANDS.choose(&mut rng).unwrap(); pb.set_message(format!("{}: {}", pkg, cmd)); pb.inc(1); thread::sleep(Duration::from_millis(rng.gen_range(25..200))); } pb.finish_with_message("waiting..."); }); } m.join_and_clear().unwrap(); println!("{} Done in {}", SPARKLE, HumanDuration(started.elapsed())); } indicatif-0.16.0/src/format.rs000064400000000000000000000064200000000000000142460ustar 00000000000000use std::fmt; use std::time::Duration; use number_prefix::NumberPrefix; /// Wraps an std duration for human basic formatting. #[derive(Debug)] pub struct FormattedDuration(pub Duration); /// Wraps an std duration for human readable formatting. #[derive(Debug)] pub struct HumanDuration(pub Duration); /// Formats bytes for human readability #[derive(Debug)] pub struct HumanBytes(pub u64); /// Formats bytes for human readability using SI prefixes #[derive(Debug)] pub struct DecimalBytes(pub u64); /// Formats bytes for human readability using ISO/IEC prefixes #[derive(Debug)] pub struct BinaryBytes(pub u64); impl fmt::Display for FormattedDuration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut t = self.0.as_secs(); let seconds = t % 60; t /= 60; let minutes = t % 60; t /= 60; let hours = t % 24; t /= 24; if t > 0 { let days = t; write!(f, "{}d {:02}:{:02}:{:02}", days, hours, minutes, seconds) } else { write!(f, "{:02}:{:02}:{:02}", hours, minutes, seconds) } } } impl fmt::Display for HumanDuration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let t = self.0.as_secs(); let alt = f.alternate(); macro_rules! try_unit { ($secs:expr, $sg:expr, $pl:expr, $s:expr) => { let cnt = t / $secs; if cnt == 1 { if alt { return write!(f, "{}{}", cnt, $s); } else { return write!(f, "{} {}", cnt, $sg); } } else if cnt > 1 { if alt { return write!(f, "{}{}", cnt, $s); } else { return write!(f, "{} {}", cnt, $pl); } } }; } try_unit!(365 * 24 * 60 * 60, "year", "years", "y"); try_unit!(7 * 24 * 60 * 60, "week", "weeks", "w"); try_unit!(24 * 60 * 60, "day", "days", "d"); try_unit!(60 * 60, "hour", "hours", "h"); try_unit!(60, "minute", "minutes", "m"); try_unit!(1, "second", "seconds", "s"); write!(f, "0{}", if alt { "s" } else { " seconds" }) } } impl fmt::Display for HumanBytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match NumberPrefix::binary(self.0 as f64) { NumberPrefix::Standalone(number) => write!(f, "{:.0}B", number), NumberPrefix::Prefixed(prefix, number) => write!(f, "{:.2}{}B", number, prefix), } } } impl fmt::Display for DecimalBytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match NumberPrefix::decimal(self.0 as f64) { NumberPrefix::Standalone(number) => write!(f, "{:.0}B", number), NumberPrefix::Prefixed(prefix, number) => write!(f, "{:.2}{}B", number, prefix), } } } impl fmt::Display for BinaryBytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match NumberPrefix::binary(self.0 as f64) { NumberPrefix::Standalone(number) => write!(f, "{:.0}B", number), NumberPrefix::Prefixed(prefix, number) => write!(f, "{:.2}{}B", number, prefix), } } } indicatif-0.16.0/src/iter.rs000064400000000000000000000121050000000000000137160ustar 00000000000000use crate::progress_bar::ProgressBar; use std::convert::TryFrom; use std::io::{self, IoSliceMut}; use std::iter::FusedIterator; /// Wraps an iterator to display its progress. pub trait ProgressIterator where Self: Sized + Iterator, { /// Wrap an iterator with default styling. Uses `Iterator::size_hint` to get length. /// Returns `Some(..)` only if `size_hint.1` is `Some`. If you want to create a progress bar /// even if `size_hint.1` returns `None` use `progress_count` or `progress_with` instead. fn try_progress(self) -> Option> { self.size_hint() .1 .map(|len| self.progress_count(u64::try_from(len).unwrap())) } /// Wrap an iterator with default styling. fn progress(self) -> ProgressBarIter where Self: ExactSizeIterator, { let len = u64::try_from(self.len()).unwrap(); self.progress_count(len) } /// Wrap an iterator with an explicit element count. fn progress_count(self, len: u64) -> ProgressBarIter { self.progress_with(ProgressBar::new(len)) } /// Wrap an iterator with a custom progress bar. fn progress_with(self, progress: ProgressBar) -> ProgressBarIter; } /// Wraps an iterator to display its progress. #[derive(Debug)] pub struct ProgressBarIter { pub(crate) it: T, pub progress: ProgressBar, } impl> Iterator for ProgressBarIter { type Item = S; fn next(&mut self) -> Option { let item = self.it.next(); if item.is_some() { self.progress.inc(1); } else if !self.progress.is_finished() { self.progress.finish_using_style(); } item } } impl ExactSizeIterator for ProgressBarIter { fn len(&self) -> usize { self.it.len() } } impl DoubleEndedIterator for ProgressBarIter { fn next_back(&mut self) -> Option { let item = self.it.next_back(); if item.is_some() { self.progress.inc(1); } else if !self.progress.is_finished() { self.progress.finish_using_style(); } item } } impl FusedIterator for ProgressBarIter {} impl io::Read for ProgressBarIter { fn read(&mut self, buf: &mut [u8]) -> io::Result { let inc = self.it.read(buf)?; self.progress.inc(inc as u64); Ok(inc) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let inc = self.it.read_vectored(bufs)?; self.progress.inc(inc as u64); Ok(inc) } fn read_to_string(&mut self, buf: &mut String) -> io::Result { let inc = self.it.read_to_string(buf)?; self.progress.inc(inc as u64); Ok(inc) } fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { self.it.read_exact(buf)?; self.progress.inc(buf.len() as u64); Ok(()) } } impl io::BufRead for ProgressBarIter { fn fill_buf(&mut self) -> io::Result<&[u8]> { self.it.fill_buf() } fn consume(&mut self, amt: usize) { self.it.consume(amt); self.progress.inc(amt as u64); } } impl io::Seek for ProgressBarIter { fn seek(&mut self, f: io::SeekFrom) -> io::Result { self.it.seek(f).map(|pos| { self.progress.set_position(pos); pos }) } } impl io::Write for ProgressBarIter { fn write(&mut self, buf: &[u8]) -> io::Result { self.it.write(buf).map(|inc| { self.progress.inc(inc as u64); inc }) } fn write_vectored(&mut self, bufs: &[io::IoSlice]) -> io::Result { self.it.write_vectored(bufs).map(|inc| { self.progress.inc(inc as u64); inc }) } fn flush(&mut self) -> io::Result<()> { self.it.flush() } fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.it.write_all(buf).map(|()| { self.progress.inc(buf.len() as u64); }) } // write_fmt can not be captured with reasonable effort. // as it uses write_all internally by default that should not be a problem. // fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()>; } impl> ProgressIterator for T { fn progress_with(self, progress: ProgressBar) -> ProgressBarIter { ProgressBarIter { it: self, progress } } } #[cfg(test)] mod test { use crate::iter::{ProgressBarIter, ProgressIterator}; use crate::progress_bar::ProgressBar; #[test] fn it_can_wrap_an_iterator() { let v = vec![1, 2, 3]; let wrap = |it: ProgressBarIter<_>| { assert_eq!(it.map(|x| x * 2).collect::>(), vec![2, 4, 6]); }; wrap(v.iter().progress()); wrap(v.iter().progress_count(3)); wrap({ let pb = ProgressBar::new(v.len() as u64); v.iter().progress_with(pb) }); } } indicatif-0.16.0/src/lib.rs000064400000000000000000000206210000000000000135230ustar 00000000000000//! indicatif is a library for Rust that helps you build command line //! interfaces that report progress to users. It comes with various //! tools and utilities for formatting anything that indicates progress. //! //! Platform support: //! //! * Linux //! * OS X //! * Windows (colors require Windows 10) //! //! Best paired with other libraries in the family: //! //! * [console](https://docs.rs/console) //! * [dialoguer](https://docs.rs/dialoguer) //! //! # Crate Contents //! //! * **Progress bars** //! * [`ProgressBar`](struct.ProgressBar.html) for bars and spinners //! * [`MultiProgress`](struct.MultiProgress.html) for multiple bars //! * **Data Formatting** //! * [`HumanBytes`](struct.HumanBytes.html) for formatting bytes //! * [`DecimalBytes`](struct.DecimalBytes.html) for formatting bytes using SI prefixes //! * [`BinaryBytes`](struct.BinaryBytes.html) for formatting bytes using ISO/IEC prefixes //! * [`HumanDuration`](struct.HumanDuration.html) for formatting durations //! //! # Progress Bars and Spinners //! //! indicatif comes with a `ProgressBar` type that supports both bounded //! progress bar uses as well as unbounded "spinner" type progress reports. //! Progress bars are `Sync` and `Send` objects which means that they are //! internally locked and can be passed from thread to thread. //! //! Additionally a `MultiProgress` utility is provided that can manage //! rendering multiple progress bars at once (eg: from multiple threads). //! //! To whet your appetite, this is what this can look like: //! //! //! //! Progress bars are manually advanced and by default draw to stderr. //! When you are done, the progress bar can be finished either visibly //! (eg: the progress bar stays on the screen) or cleared (the progress //! bar will be removed). //! //! ```rust //! use indicatif::ProgressBar; //! //! let bar = ProgressBar::new(1000); //! for _ in 0..1000 { //! bar.inc(1); //! // ... //! } //! bar.finish(); //! ``` //! //! General progress bar behaviors: //! //! * if a non terminal is detected the progress bar will be completely //! hidden. This makes piping programs to logfiles make sense out of //! the box. //! * a progress bar only starts drawing when `set_message`, `inc`, `set_position` //! or `tick` are called. In some situations you might have to call `tick` //! once to draw it. //! * progress bars should be explicitly finished to reset the rendering //! for others. Either by also clearing them or by replacing them with //! a new message / retaining the current message. //! * the default template renders neither message nor prefix. //! //! # Iterators //! //! Similar to [tqdm](https://github.com/tqdm/tqdm), progress bars can be //! associated with an iterator. For example: //! //! ```rust //! use indicatif::ProgressIterator; //! //! for _ in (0..1000).progress() { //! // ... //! } //! ``` //! //! See the [`ProgressIterator`](trait.ProgressIterator.html) trait for more //! methods to configure the number of elements in the iterator or change //! the progress bar style. Indicatif also has optional support for parallel //! iterators with [Rayon](https://github.com/rayon-rs/rayon). In your //! `Cargo.toml`, use the "rayon" feature: //! //! ```toml //! [dependencies] //! indicatif = {version = "*", features = ["rayon"]} //! ``` //! //! And then use it like this: //! //! ```rust,ignore //! # extern crate rayon; //! use indicatif::ParallelProgressIterator; //! use rayon::iter::{ParallelIterator, IntoParallelRefIterator}; //! //! let v: Vec<_> = (0..100000).collect(); //! let v2: Vec<_> = v.par_iter().progress_count(v.len() as u64).map(|i| i + 1).collect(); //! assert_eq!(v2[0], 1); //! ``` //! //! Or if you'd like to customize the progress bar: //! //! ```rust,ignore //! # extern crate rayon; //! use indicatif::{ProgressBar, ParallelProgressIterator}; //! use rayon::iter::{ParallelIterator, IntoParallelRefIterator}; //! //! // Use `ProgressBar::with_style` to change the view //! let pb = ProgressBar::new(); //! let v: Vec<_> = (0..100000).collect(); //! let v2: Vec<_> = v.par_iter().progress_with(pb).map(|i| i + 1).collect(); //! assert_eq!(v2[0], 1); //! ``` //! //! # Templates //! //! Progress bars can be styled with simple format strings similar to the //! ones in Rust itself. The format for a placeholder is `{key:options}` //! where the `options` part is optional. If provided the format is this: //! //! ```text //! [<^>] for an optional alignment specification //! WIDTH an optional width as positive integer //! ! an optional exclamation mark to enable truncation //! .STYLE an optional dot separated style string //! /STYLE an optional dot separated alternative style string //! ``` //! //! For the style component see [`Style::from_dotted_str`](https://docs.rs/console/0.7.5/console/struct.Style.html#method.from_dotted_str) //! for more information. Indicatif uses the `console` base crate for all //! colorization and formatting options. //! //! Some examples for templates: //! //! ```text //! [{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg} //! ``` //! //! This sets a progress bar that is 40 characters wide and has cyan //! as primary style color and blue as alternative style color. //! Alternative styles are currently only used for progress bars. //! //! Example configuration: //! //! ```rust //! # use indicatif::{ProgressBar, ProgressStyle}; //! # let bar = ProgressBar::new(0); //! bar.set_style(ProgressStyle::default_bar() //! .template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}") //! .progress_chars("##-")); //! ``` //! //! The following keys exist: //! //! * `bar`: renders a progress bar. By default 20 characters wide. The //! style string is used to color the elapsed part, the alternative //! style is used for the bar that is yet to render. //! * `wide_bar`: like `bar` but always fills the remaining space. //! * `spinner`: renders the spinner (current tick string). //! * `prefix`: renders the prefix set on the progress bar. //! * `msg`: renders the currently set message on the progress bar. //! * `wide_msg`: like `msg` but always fills the remaining space and truncates. //! * `pos`: renders the current position of the bar as integer //! * `len`: renders the total length of the bar as integer //! * `bytes`: renders the current position of the bar as bytes. //! * `percent`: renders the current position of the bar as a percentage of the total length. //! * `total_bytes`: renders the total length of the bar as bytes. //! * `elapsed_precise`: renders the elapsed time as `HH:MM:SS`. //! * `elapsed`: renders the elapsed time as `42s`, `1m` etc. //! * `per_sec`: renders the speed in steps per second. //! * `bytes_per_sec`: renders the speed in bytes per second. //! * `binary_bytes_per_sec`: renders the speed in bytes per second using //! power-of-two units, i.e. `MiB`, `KiB`, etc. //! * `eta_precise`: the remaining time (like `elapsed_precise`). //! * `eta`: the remaining time (like `elapsed`). //! * `duration_precise`: the extrapolated total duration (like `elapsed_precise`). //! * `duration`: the extrapolated total duration time (like `elapsed`). //! //! The design of the progress bar can be altered with the integrated //! template functionality. The template can be set by changing a //! `ProgressStyle` and attaching it to the progress bar. //! //! # Human Readable Formatting //! //! There are some formatting wrappers for showing elapsed time and //! file sizes for human users: //! //! ```rust //! # use std::time::Duration; //! use indicatif::{HumanDuration, HumanBytes}; //! //! assert_eq!("3.00MiB", HumanBytes(3*1024*1024).to_string()); //! assert_eq!("8 seconds", HumanDuration(Duration::from_secs(8)).to_string()); //! ``` //! //! # Feature Flags //! //! * `rayon`: adds rayon support //! * `improved_unicode`: adds improved unicode support (graphemes, better width calculation) mod format; mod iter; mod progress_bar; #[cfg(feature = "rayon")] mod rayon; mod state; mod style; mod utils; pub use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration}; pub use crate::iter::{ProgressBarIter, ProgressIterator}; pub use crate::progress_bar::{MultiProgress, ProgressBar, WeakProgressBar}; pub use crate::state::ProgressDrawTarget; pub use crate::style::{ProgressFinish, ProgressStyle}; #[cfg(feature = "rayon")] pub use crate::rayon::ParallelProgressIterator; indicatif-0.16.0/src/progress_bar.rs000064400000000000000000000762430000000000000154600ustar 00000000000000use std::borrow::Cow; use std::fmt; use std::io; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::{Arc, Mutex, RwLock, Weak}; use std::thread; use std::time::{Duration, Instant}; use crate::state::{ MultiObject, MultiProgressState, ProgressDrawState, ProgressDrawTarget, ProgressDrawTargetKind, ProgressState, Status, }; use crate::style::ProgressStyle; use crate::utils::Estimate; use crate::{ProgressBarIter, ProgressIterator}; /// A progress bar or spinner. /// /// The progress bar is an `Arc` around an internal state. When the progress /// bar is cloned it just increments the refcount which means the bar is /// shared with the original one. #[derive(Clone)] pub struct ProgressBar { state: Arc>, } impl fmt::Debug for ProgressBar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ProgressBar").finish() } } impl ProgressBar { /// Creates a new progress bar with a given length. /// /// This progress bar by default draws directly to stderr, and refreshes /// a maximum of 15 times a second. To change the refresh rate set the /// draw target to one with a different refresh rate. pub fn new(len: u64) -> ProgressBar { ProgressBar::with_draw_target(len, ProgressDrawTarget::stderr()) } /// Creates a completely hidden progress bar. /// /// This progress bar still responds to API changes but it does not /// have a length or render in any way. pub fn hidden() -> ProgressBar { ProgressBar::with_draw_target(!0, ProgressDrawTarget::hidden()) } /// Creates a new progress bar with a given length and draw target. pub fn with_draw_target(len: u64, target: ProgressDrawTarget) -> ProgressBar { ProgressBar { state: Arc::new(Mutex::new(ProgressState { style: ProgressStyle::default_bar(), draw_target: target, message: "".into(), prefix: "".into(), pos: 0, len, tick: 0, draw_delta: 0, draw_rate: 0, draw_next: 0, status: Status::InProgress, started: Instant::now(), est: Estimate::new(), tick_thread: None, steady_tick: 0, })), } } /// A convenience builder-like function for a progress bar with a given style. pub fn with_style(self, style: ProgressStyle) -> ProgressBar { self.state.lock().unwrap().style = style; self } /// A convenience builder-like function for a progress bar with a given prefix. pub fn with_prefix(self, prefix: impl Into>) -> ProgressBar { self.state.lock().unwrap().prefix = prefix.into(); self } /// A convenience builder-like function for a progress bar with a given message. pub fn with_message(self, message: impl Into>) -> ProgressBar { self.state.lock().unwrap().message = message.into(); self } /// A convenience builder-like function for a progress bar with a given position. pub fn with_position(self, pos: u64) -> ProgressBar { self.state.lock().unwrap().pos = pos; self } /// Creates a new spinner. /// /// This spinner by default draws directly to stderr. This adds the /// default spinner style to it. pub fn new_spinner() -> ProgressBar { let rv = ProgressBar::new(!0); rv.set_style(ProgressStyle::default_spinner()); rv } /// Overrides the stored style. /// /// This does not redraw the bar. Call `tick` to force it. pub fn set_style(&self, style: ProgressStyle) { self.state.lock().unwrap().style = style; } /// Spawns a background thread to tick the progress bar. /// /// When this is enabled a background thread will regularly tick the /// progress back in the given interval (milliseconds). This is /// useful to advance progress bars that are very slow by themselves. /// /// When steady ticks are enabled calling `.tick()` on a progress /// bar does not do anything. pub fn enable_steady_tick(&self, ms: u64) { let mut state = self.state.lock().unwrap(); state.steady_tick = ms; if state.tick_thread.is_some() { return; } // Using a weak pointer is required to prevent a potential deadlock. See issue #133 let state_arc = Arc::downgrade(&self.state); state.tick_thread = Some(thread::spawn(move || Self::steady_tick(state_arc, ms))); ::std::mem::drop(state); // use the side effect of tick to force the bar to tick. self.tick(); } fn steady_tick(state_arc: Weak>, mut ms: u64) { loop { thread::sleep(Duration::from_millis(ms)); if let Some(state_arc) = state_arc.upgrade() { let mut state = state_arc.lock().unwrap(); if state.is_finished() || state.steady_tick == 0 { state.steady_tick = 0; state.tick_thread = None; break; } if state.tick != 0 { state.tick = state.tick.saturating_add(1); } ms = state.steady_tick; state.draw().ok(); } else { break; } } } /// Undoes `enable_steady_tick`. pub fn disable_steady_tick(&self) { self.enable_steady_tick(0); } /// Limit redrawing of progress bar to every `n` steps. Defaults to 0. /// /// By default, the progress bar will redraw whenever its state advances. /// This setting is helpful in situations where the overhead of redrawing /// the progress bar dominates the computation whose progress is being /// reported. /// /// If `n` is greater than 0, operations that change the progress bar such /// as `.tick()`, `.set_message()` and `.set_length()` will no longer cause /// the progress bar to be redrawn, and will only be shown once the /// position advances by `n` steps. /// /// ```rust,no_run /// # use indicatif::ProgressBar; /// let n = 1_000_000; /// let pb = ProgressBar::new(n); /// pb.set_draw_delta(n / 100); // redraw every 1% of additional progress /// ``` /// /// Note that `ProgressDrawTarget` may impose additional buffering of redraws. pub fn set_draw_delta(&self, n: u64) { let mut state = self.state.lock().unwrap(); state.draw_delta = n; state.draw_next = state.pos.saturating_add(state.draw_delta); } /// Sets the refresh rate of progress bar to `n` updates per seconds. Defaults to 0. /// /// This is similar to `set_draw_delta` but automatically adapts to a constant refresh rate /// regardless of how consistent the progress is. /// /// This parameter takes precedence on `set_draw_delta` if different from 0. /// /// ```rust,no_run /// # use indicatif::ProgressBar; /// let n = 1_000_000; /// let pb = ProgressBar::new(n); /// pb.set_draw_rate(25); // aims at redrawing at most 25 times per seconds. /// ``` /// /// Note that `ProgressDrawTarget` may impose additional buffering of redraws. pub fn set_draw_rate(&self, n: u64) { let mut state = self.state.lock().unwrap(); state.draw_rate = n; state.draw_next = state.pos.saturating_add(state.per_sec() / n); } /// Manually ticks the spinner or progress bar. /// /// This automatically happens on any other change to a progress bar. pub fn tick(&self) { self.update_and_draw(|state| { if state.steady_tick == 0 || state.tick == 0 { state.tick = state.tick.saturating_add(1); } }); } /// Advances the position of a progress bar by delta. pub fn inc(&self, delta: u64) { self.update_and_draw(|state| { state.pos = state.pos.saturating_add(delta); if state.steady_tick == 0 || state.tick == 0 { state.tick = state.tick.saturating_add(1); } }) } /// A quick convenience check if the progress bar is hidden. pub fn is_hidden(&self) -> bool { self.state.lock().unwrap().draw_target.is_hidden() } /// Indicates that the progress bar finished. pub fn is_finished(&self) -> bool { self.state.lock().unwrap().is_finished() } /// Print a log line above the progress bar. /// /// If the progress bar was added to a `MultiProgress`, the log line will be /// printed above all other progress bars. /// /// Note that if the progress bar is hidden (which by default happens if /// the progress bar is redirected into a file) println will not do /// anything either. pub fn println>(&self, msg: I) { let mut state = self.state.lock().unwrap(); let mut lines: Vec = msg.as_ref().lines().map(Into::into).collect(); let orphan_lines = lines.len(); if state.should_render() && !state.draw_target.is_hidden() { lines.extend(state.style.format_state(&*state)); } let draw_state = ProgressDrawState { lines, orphan_lines, finished: state.is_finished(), force_draw: true, move_cursor: false, }; state.draw_target.apply_draw_state(draw_state).ok(); } /// Sets the position of the progress bar. pub fn set_position(&self, pos: u64) { self.update_and_draw(|state| { state.draw_next = pos; state.pos = pos; if state.steady_tick == 0 || state.tick == 0 { state.tick = state.tick.saturating_add(1); } }) } /// Sets the length of the progress bar. pub fn set_length(&self, len: u64) { self.update_and_draw(|state| { state.len = len; }) } /// Increase the length of the progress bar. pub fn inc_length(&self, delta: u64) { self.update_and_draw(|state| { state.len = state.len.saturating_add(delta); }) } /// Sets the current prefix of the progress bar. /// /// For the prefix to be visible, `{prefix}` placeholder /// must be present in the template (see `ProgressStyle`). pub fn set_prefix(&self, prefix: impl Into>) { let prefix = prefix.into(); self.update_and_draw(|state| { state.prefix = prefix; if state.steady_tick == 0 || state.tick == 0 { state.tick = state.tick.saturating_add(1); } }) } /// Sets the current message of the progress bar. /// /// For the message to be visible, `{msg}` placeholder /// must be present in the template (see `ProgressStyle`). pub fn set_message(&self, msg: impl Into>) { let msg = msg.into(); self.update_and_draw(|state| { state.message = msg; if state.steady_tick == 0 || state.tick == 0 { state.tick = state.tick.saturating_add(1); } }) } /// Creates a new weak reference to this `ProgressBar`. pub fn downgrade(&self) -> WeakProgressBar { WeakProgressBar { state: Arc::downgrade(&self.state), } } /// Resets the ETA calculation. /// /// This can be useful if progress bars make a huge jump or were /// paused for a prolonged time. pub fn reset_eta(&self) { self.update_and_draw(|state| { state.est.reset(state.pos); }); } /// Resets elapsed time pub fn reset_elapsed(&self) { self.update_and_draw(|state| { state.started = Instant::now(); }); } pub fn reset(&self) { self.reset_eta(); self.reset_elapsed(); self.update_and_draw(|state| { state.draw_next = 0; state.pos = 0; state.status = Status::InProgress; }); } /// Finishes the progress bar and leaves the current message. pub fn finish(&self) { self.state.lock().unwrap().finish(); } /// Finishes the progress bar at current position and leaves the current message. pub fn finish_at_current_pos(&self) { self.state.lock().unwrap().finish_at_current_pos(); } /// Finishes the progress bar and sets a message. /// /// For the message to be visible, `{msg}` placeholder /// must be present in the template (see `ProgressStyle`). pub fn finish_with_message(&self, msg: impl Into>) { self.state.lock().unwrap().finish_with_message(msg); } /// Finishes the progress bar and completely clears it. pub fn finish_and_clear(&self) { self.state.lock().unwrap().finish_and_clear(); } /// Finishes the progress bar and leaves the current message and progress. pub fn abandon(&self) { self.state.lock().unwrap().abandon(); } /// Finishes the progress bar and sets a message, and leaves the current progress. /// /// For the message to be visible, `{msg}` placeholder /// must be present in the template (see `ProgressStyle`). pub fn abandon_with_message(&self, msg: impl Into>) { self.state.lock().unwrap().abandon_with_message(msg); } /// Finishes the progress bar using the [`ProgressFinish`] behavior stored /// in the [`ProgressStyle`]. pub fn finish_using_style(&self) { self.state.lock().unwrap().finish_using_style(); } /// Sets a different draw target for the progress bar. /// /// This can be used to draw the progress bar to stderr /// for instance: /// /// ```rust,no_run /// # use indicatif::{ProgressBar, ProgressDrawTarget}; /// let pb = ProgressBar::new(100); /// pb.set_draw_target(ProgressDrawTarget::stderr()); /// ``` /// /// **Note:** Calling this method on a `ProgressBar` linked with a `MultiProgress` (i.e. after /// running `MultiProgress::add`) will unlink this progress bar. If you don't want this behavior, /// call `MultiProgress::set_draw_target` instead. pub fn set_draw_target(&self, target: ProgressDrawTarget) { let mut state = self.state.lock().unwrap(); state.draw_target.disconnect(); state.draw_target = target; } /// Wraps an iterator with the progress bar. /// /// ```rust,no_run /// # use indicatif::ProgressBar; /// let v = vec![1, 2, 3]; /// let pb = ProgressBar::new(3); /// for item in pb.wrap_iter(v.iter()) { /// // ... /// } /// ``` pub fn wrap_iter(&self, it: It) -> ProgressBarIter { it.progress_with(self.clone()) } /// Wraps a Reader with the progress bar. /// /// ```rust,no_run /// # use std::fs::File; /// # use std::io; /// # use indicatif::ProgressBar; /// # fn test () -> io::Result<()> { /// let source = File::open("work.txt")?; /// let mut target = File::create("done.txt")?; /// let pb = ProgressBar::new(source.metadata()?.len()); /// io::copy(&mut pb.wrap_read(source), &mut target); /// # Ok(()) /// # } /// ``` pub fn wrap_read(&self, read: R) -> ProgressBarIter { ProgressBarIter { progress: self.clone(), it: read, } } /// Wraps a Writer with the progress bar. /// /// ```rust,no_run /// # use std::fs::File; /// # use std::io; /// # use indicatif::ProgressBar; /// # fn test () -> io::Result<()> { /// let mut source = File::open("work.txt")?; /// let target = File::create("done.txt")?; /// let pb = ProgressBar::new(source.metadata()?.len()); /// io::copy(&mut source, &mut pb.wrap_write(target)); /// # Ok(()) /// # } /// ``` pub fn wrap_write(&self, write: W) -> ProgressBarIter { ProgressBarIter { progress: self.clone(), it: write, } } fn update_and_draw(&self, f: F) { // Delegate to the wrapped state. let mut state = self.state.lock().unwrap(); state.update_and_draw(f); } pub fn position(&self) -> u64 { self.state.lock().unwrap().pos } pub fn length(&self) -> u64 { self.state.lock().unwrap().len } pub fn eta(&self) -> Duration { self.state.lock().unwrap().eta() } pub fn per_sec(&self) -> u64 { self.state.lock().unwrap().per_sec() } pub fn duration(&self) -> Duration { self.state.lock().unwrap().duration() } pub fn elapsed(&self) -> Duration { self.state.lock().unwrap().started.elapsed() } } /// Manages multiple progress bars from different threads. pub struct MultiProgress { state: Arc>, joining: AtomicBool, tx: Sender<(usize, ProgressDrawState)>, rx: Receiver<(usize, ProgressDrawState)>, } impl fmt::Debug for MultiProgress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MultiProgress").finish() } } unsafe impl Sync for MultiProgress {} impl Default for MultiProgress { fn default() -> MultiProgress { MultiProgress::with_draw_target(ProgressDrawTarget::stderr()) } } impl MultiProgress { /// Creates a new multi progress object. /// /// Progress bars added to this object by default draw directly to stderr, and refresh /// a maximum of 15 times a second. To change the refresh rate set the draw target to /// one with a different refresh rate. pub fn new() -> MultiProgress { MultiProgress::default() } /// Creates a new multi progress object with the given draw target. pub fn with_draw_target(draw_target: ProgressDrawTarget) -> MultiProgress { let (tx, rx) = channel(); MultiProgress { state: Arc::new(RwLock::new(MultiProgressState { objects: Vec::new(), free_set: Vec::new(), ordering: vec![], draw_target, move_cursor: false, })), joining: AtomicBool::new(false), tx, rx, } } /// Sets a different draw target for the multiprogress bar. pub fn set_draw_target(&self, target: ProgressDrawTarget) { let mut state = self.state.write().unwrap(); state.draw_target.disconnect(); state.draw_target = target; } /// Set whether we should try to move the cursor when possible instead of clearing lines. /// /// This can reduce flickering, but do not enable it if you intend to change the number of /// progress bars. pub fn set_move_cursor(&self, move_cursor: bool) { self.state.write().unwrap().move_cursor = move_cursor; } /// Adds a progress bar. /// /// The progress bar added will have the draw target changed to a /// remote draw target that is intercepted by the multi progress /// object overriding custom `ProgressDrawTarget` settings. pub fn add(&self, pb: ProgressBar) -> ProgressBar { self.push(None, pb) } /// Inserts a progress bar. /// /// The progress bar inserted at position `index` will have the draw /// target changed to a remote draw target that is intercepted by the /// multi progress object overriding custom `ProgressDrawTarget` settings. /// /// If `index >= MultiProgressState::objects.len()`, the progress bar /// is added to the end of the list. pub fn insert(&self, index: usize, pb: ProgressBar) -> ProgressBar { self.push(Some(index), pb) } fn push(&self, pos: Option, pb: ProgressBar) -> ProgressBar { let new = MultiObject { done: false, draw_state: None, }; let mut state = self.state.write().unwrap(); let idx = match state.free_set.pop() { Some(idx) => { state.objects[idx] = Some(new); idx } None => { state.objects.push(Some(new)); state.objects.len() - 1 } }; match pos { Some(pos) if pos < state.ordering.len() => state.ordering.insert(pos, idx), _ => state.ordering.push(idx), } pb.set_draw_target(ProgressDrawTarget { kind: ProgressDrawTargetKind::Remote { state: self.state.clone(), idx, chan: Mutex::new(self.tx.clone()), }, }); pb } /// Removes a progress bar. /// /// The progress bar is removed only if it was previously inserted or added /// by the methods `MultiProgress::insert` or `MultiProgress::add`. /// If the passed progress bar does not satisfy the condition above, /// the `remove` method does nothing. pub fn remove(&self, pb: &ProgressBar) { let idx = match &pb.state.lock().unwrap().draw_target.kind { ProgressDrawTargetKind::Remote { state, idx, .. } => { // Check that this progress bar is owned by the current MultiProgress. assert!(Arc::ptr_eq(&self.state, state)); *idx } _ => return, }; self.state.write().unwrap().remove_idx(idx); } /// Waits for all progress bars to report that they are finished. /// /// You need to call this as this will request the draw instructions /// from the remote progress bars. Not calling this will deadlock /// your program. pub fn join(&self) -> io::Result<()> { self.join_impl(false) } /// Works like `join` but clears the progress bar in the end. pub fn join_and_clear(&self) -> io::Result<()> { self.join_impl(true) } fn join_impl(&self, clear: bool) -> io::Result<()> { if self.joining.load(Ordering::Acquire) { panic!("Already joining!"); } self.joining.store(true, Ordering::Release); let move_cursor = self.state.read().unwrap().move_cursor; // Max amount of grouped together updates at once. This is meant // to ensure there isn't a situation where continuous updates prevent // any actual draws happening. const MAX_GROUP_SIZE: usize = 32; let mut recv_peek = None; let mut grouped = 0usize; let mut orphan_lines: Vec = Vec::new(); let mut force_draw = false; while !self.state.read().unwrap().is_done() { let (idx, draw_state) = if let Some(peeked) = recv_peek.take() { peeked } else { self.rx.recv().unwrap() }; force_draw |= draw_state.finished || draw_state.force_draw; let mut state = self.state.write().unwrap(); if draw_state.finished { if let Some(ref mut obj) = &mut state.objects[idx] { obj.done = true; } if draw_state.lines.is_empty() { // `finish_and_clear` was called state.remove_idx(idx); } } // Split orphan lines out of the draw state, if any let lines = if draw_state.orphan_lines > 0 { let split = draw_state.lines.split_at(draw_state.orphan_lines); orphan_lines.extend_from_slice(split.0); split.1.to_vec() } else { draw_state.lines }; let draw_state = ProgressDrawState { lines, orphan_lines: 0, ..draw_state }; if let Some(ref mut obj) = &mut state.objects[idx] { obj.draw_state = Some(draw_state); } // the rest from here is only drawing, we can skip it. if state.draw_target.is_hidden() { continue; } debug_assert!(recv_peek.is_none()); if grouped >= MAX_GROUP_SIZE { // Can't group any more draw calls, proceed to just draw grouped = 0; } else if let Ok(state) = self.rx.try_recv() { // Only group draw calls if there is another draw already queued recv_peek = Some(state); grouped += 1; continue; } else { // No more draws queued, proceed to just draw grouped = 0; } let mut lines = vec![]; // Make orphaned lines appear at the top, so they can be properly // forgotten. let orphan_lines_count = orphan_lines.len(); lines.append(&mut orphan_lines); for index in state.ordering.iter() { if let Some(obj) = &state.objects[*index] { if let Some(ref draw_state) = obj.draw_state { lines.extend_from_slice(&draw_state.lines[..]); } } } let finished = state.is_done(); state.draw_target.apply_draw_state(ProgressDrawState { lines, orphan_lines: orphan_lines_count, force_draw: force_draw || orphan_lines_count > 0, move_cursor, finished, })?; force_draw = false; } if clear { let mut state = self.state.write().unwrap(); state.draw_target.apply_draw_state(ProgressDrawState { lines: vec![], orphan_lines: 0, finished: true, force_draw: true, move_cursor, })?; } self.joining.store(false, Ordering::Release); Ok(()) } } /// A weak reference to a `ProgressBar`. /// /// Useful for creating custom steady tick implementations #[derive(Clone)] pub struct WeakProgressBar { state: Weak>, } impl WeakProgressBar { /// Attempts to upgrade the Weak pointer to a [`ProgressBar`], delaying dropping of the inner /// value if successful. Returns `None` if the inner value has since been dropped. /// /// [`ProgressBar`]: struct.ProgressBar.html pub fn upgrade(&self) -> Option { self.state.upgrade().map(|state| ProgressBar { state }) } } #[cfg(test)] mod tests { use super::*; #[allow(clippy::float_cmp)] #[test] fn test_pbar_zero() { let pb = ProgressBar::new(0); assert_eq!(pb.state.lock().unwrap().fraction(), 1.0); } #[allow(clippy::float_cmp)] #[test] fn test_pbar_maxu64() { let pb = ProgressBar::new(!0); assert_eq!(pb.state.lock().unwrap().fraction(), 0.0); } #[test] fn test_pbar_overflow() { let pb = ProgressBar::new(1); pb.set_draw_target(ProgressDrawTarget::hidden()); pb.inc(2); pb.finish(); } #[test] fn test_get_position() { let pb = ProgressBar::new(1); pb.set_draw_target(ProgressDrawTarget::hidden()); pb.inc(2); let pos = pb.position(); assert_eq!(pos, 2); } #[test] fn test_weak_pb() { let pb = ProgressBar::new(0); let weak = pb.downgrade(); assert!(weak.upgrade().is_some()); ::std::mem::drop(pb); assert!(weak.upgrade().is_none()); } #[test] fn test_draw_delta_deadlock() { // see issue #187 let mpb = MultiProgress::new(); let pb = mpb.add(ProgressBar::new(1)); pb.set_draw_delta(2); drop(pb); mpb.join().unwrap(); } #[test] fn test_abandon_deadlock() { let mpb = MultiProgress::new(); let pb = mpb.add(ProgressBar::new(1)); pb.set_draw_delta(2); pb.abandon(); drop(pb); mpb.join().unwrap(); } #[test] fn late_pb_drop() { let pb = ProgressBar::new(10); let mpb = MultiProgress::new(); // This clone call is required to trigger a now fixed bug. // See for context #[allow(clippy::redundant_clone)] mpb.add(pb.clone()); } #[test] fn it_can_wrap_a_reader() { let bytes = &b"I am an implementation of io::Read"[..]; let pb = ProgressBar::new(bytes.len() as u64); let mut reader = pb.wrap_read(bytes); let mut writer = Vec::new(); io::copy(&mut reader, &mut writer).unwrap(); assert_eq!(writer, bytes); } #[test] fn it_can_wrap_a_writer() { let bytes = b"implementation of io::Read"; let mut reader = &bytes[..]; let pb = ProgressBar::new(bytes.len() as u64); let writer = Vec::new(); let mut writer = pb.wrap_write(writer); io::copy(&mut reader, &mut writer).unwrap(); assert_eq!(writer.it, bytes); } #[test] fn progress_bar_sync_send() { let _: Box = Box::new(ProgressBar::new(1)); let _: Box = Box::new(ProgressBar::new(1)); let _: Box = Box::new(MultiProgress::new()); let _: Box = Box::new(MultiProgress::new()); } #[test] fn multi_progress_modifications() { let mp = MultiProgress::new(); let p0 = mp.add(ProgressBar::new(1)); let p1 = mp.add(ProgressBar::new(1)); let p2 = mp.add(ProgressBar::new(1)); let p3 = mp.add(ProgressBar::new(1)); mp.remove(&p2); mp.remove(&p1); let p4 = mp.insert(1, ProgressBar::new(1)); let state = mp.state.read().unwrap(); // the removed place for p1 is reused assert_eq!(state.objects.len(), 4); assert_eq!(state.objects.iter().filter(|o| o.is_some()).count(), 3); // free_set may contain 1 or 2 match state.free_set.last() { Some(1) => { assert_eq!(state.ordering, vec![0, 2, 3]); assert_eq!(extract_index(&p4), 2); } Some(2) => { assert_eq!(state.ordering, vec![0, 1, 3]); assert_eq!(extract_index(&p4), 1); } _ => unreachable!(), } assert_eq!(extract_index(&p0), 0); assert_eq!(extract_index(&p1), 1); assert_eq!(extract_index(&p2), 2); assert_eq!(extract_index(&p3), 3); } #[test] fn multi_progress_multiple_remove() { let mp = MultiProgress::new(); let p0 = mp.add(ProgressBar::new(1)); let p1 = mp.add(ProgressBar::new(1)); // double remove beyond the first one have no effect mp.remove(&p0); mp.remove(&p0); mp.remove(&p0); let state = mp.state.read().unwrap(); // the removed place for p1 is reused assert_eq!(state.objects.len(), 2); assert_eq!(state.objects.iter().filter(|obj| obj.is_some()).count(), 1); assert_eq!(state.free_set.last(), Some(&0)); assert_eq!(state.ordering, vec![1]); assert_eq!(extract_index(&p0), 0); assert_eq!(extract_index(&p1), 1); } fn extract_index(pb: &ProgressBar) -> usize { match pb.state.lock().unwrap().draw_target.kind { ProgressDrawTargetKind::Remote { idx, .. } => idx, _ => unreachable!(), } } } indicatif-0.16.0/src/rayon.rs000064400000000000000000000134730000000000000141140ustar 00000000000000use crate::{ProgressBar, ProgressBarIter}; use rayon::iter::{ plumbing::{Consumer, Folder, Producer, ProducerCallback, UnindexedConsumer}, IndexedParallelIterator, ParallelIterator, }; use std::convert::TryFrom; /// Wraps a Rayon parallel iterator. /// /// See [`ProgressIterator`](trait.ProgressIterator.html) for method /// documentation. pub trait ParallelProgressIterator where Self: Sized + ParallelIterator, { /// Wrap an iterator with a custom progress bar. fn progress_with(self, progress: ProgressBar) -> ProgressBarIter; /// Wrap an iterator with an explicit element count. fn progress_count(self, len: u64) -> ProgressBarIter { self.progress_with(ProgressBar::new(len)) } fn progress(self) -> ProgressBarIter where Self: IndexedParallelIterator, { let len = u64::try_from(self.len()).unwrap(); self.progress_count(len) } } impl> ParallelProgressIterator for T { fn progress_with(self, progress: ProgressBar) -> ProgressBarIter { ProgressBarIter { it: self, progress } } } impl> IndexedParallelIterator for ProgressBarIter { fn len(&self) -> usize { self.it.len() } fn drive>(self, consumer: C) -> >::Result { let consumer = ProgressConsumer::new(consumer, self.progress); self.it.drive(consumer) } fn with_producer>( self, callback: CB, ) -> >::Output { return self.it.with_producer(Callback { callback, progress: self.progress, }); struct Callback { callback: CB, progress: ProgressBar, } impl> ProducerCallback for Callback { type Output = CB::Output; fn callback

(self, base: P) -> CB::Output where P: Producer, { let producer = ProgressProducer { base, progress: self.progress, }; self.callback.callback(producer) } } } } struct ProgressProducer { base: T, progress: ProgressBar, } impl> Producer for ProgressProducer

{ type Item = T; type IntoIter = ProgressBarIter; fn into_iter(self) -> Self::IntoIter { ProgressBarIter { it: self.base.into_iter(), progress: self.progress, } } fn min_len(&self) -> usize { self.base.min_len() } fn max_len(&self) -> usize { self.base.max_len() } fn split_at(self, index: usize) -> (Self, Self) { let (left, right) = self.base.split_at(index); ( ProgressProducer { base: left, progress: self.progress.clone(), }, ProgressProducer { base: right, progress: self.progress, }, ) } } struct ProgressConsumer { base: C, progress: ProgressBar, } impl ProgressConsumer { fn new(base: C, progress: ProgressBar) -> Self { ProgressConsumer { base, progress } } } impl> Consumer for ProgressConsumer { type Folder = ProgressFolder; type Reducer = C::Reducer; type Result = C::Result; fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { let (left, right, reducer) = self.base.split_at(index); ( ProgressConsumer::new(left, self.progress.clone()), ProgressConsumer::new(right, self.progress), reducer, ) } fn into_folder(self) -> Self::Folder { ProgressFolder { base: self.base.into_folder(), progress: self.progress, } } fn full(&self) -> bool { self.base.full() } } impl> UnindexedConsumer for ProgressConsumer { fn split_off_left(&self) -> Self { ProgressConsumer::new(self.base.split_off_left(), self.progress.clone()) } fn to_reducer(&self) -> Self::Reducer { self.base.to_reducer() } } struct ProgressFolder { base: C, progress: ProgressBar, } impl> Folder for ProgressFolder { type Result = C::Result; fn consume(self, item: T) -> Self { self.progress.inc(1); ProgressFolder { base: self.base.consume(item), progress: self.progress, } } fn complete(self) -> C::Result { if !self.progress.is_finished() { self.progress.finish_using_style(); } self.base.complete() } fn full(&self) -> bool { self.base.full() } } impl> ParallelIterator for ProgressBarIter { type Item = S; fn drive_unindexed>(self, consumer: C) -> C::Result { let consumer1 = ProgressConsumer::new(consumer, self.progress.clone()); self.it.drive_unindexed(consumer1) } } #[cfg(test)] mod test { use crate::{ParallelProgressIterator, ProgressBar, ProgressBarIter}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; #[test] fn it_can_wrap_a_parallel_iterator() { let v = vec![1, 2, 3]; fn wrap<'a, T: ParallelIterator>(it: ProgressBarIter) { assert_eq!(it.map(|x| x * 2).collect::>(), vec![2, 4, 6]); } wrap(v.par_iter().progress_count(3)); wrap({ let pb = ProgressBar::new(v.len() as u64); v.par_iter().progress_with(pb) }); } } indicatif-0.16.0/src/state.rs000064400000000000000000000424200000000000000140760ustar 00000000000000use std::borrow::Cow; use std::io; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex, RwLock}; use std::thread; use std::time::{Duration, Instant}; use crate::style::{ProgressFinish, ProgressStyle}; use crate::utils::{duration_to_secs, secs_to_duration, Estimate}; use console::Term; /// The state of a progress bar at a moment in time. pub(crate) struct ProgressState { pub(crate) style: ProgressStyle, pub(crate) pos: u64, pub(crate) len: u64, pub(crate) tick: u64, pub(crate) started: Instant, pub(crate) draw_target: ProgressDrawTarget, pub(crate) message: Cow<'static, str>, pub(crate) prefix: Cow<'static, str>, pub(crate) draw_delta: u64, pub(crate) draw_rate: u64, pub(crate) draw_next: u64, pub(crate) status: Status, pub(crate) est: Estimate, pub(crate) tick_thread: Option>, pub(crate) steady_tick: u64, } impl ProgressState { /// Returns the string that should be drawn for the /// current spinner string. pub fn current_tick_str(&self) -> &str { if self.is_finished() { self.style.get_final_tick_str() } else { self.style.get_tick_str(self.tick) } } /// Indicates that the progress bar finished. pub fn is_finished(&self) -> bool { match self.status { Status::InProgress => false, Status::DoneVisible => true, Status::DoneHidden => true, } } /// Returns `false` if the progress bar should no longer be /// drawn. pub fn should_render(&self) -> bool { !matches!(self.status, Status::DoneHidden) } /// Returns the completion as a floating-point number between 0 and 1 pub fn fraction(&self) -> f32 { let pct = match (self.pos, self.len) { (_, 0) => 1.0, (0, _) => 0.0, (pos, len) => pos as f32 / len as f32, }; pct.max(0.0).min(1.0) } /// Returns the position of the status bar as `(pos, len)` tuple. pub fn position(&self) -> (u64, u64) { (self.pos, self.len) } /// Returns the current message of the progress bar. pub fn message(&self) -> &str { &self.message } /// Returns the current prefix of the progress bar. pub fn prefix(&self) -> &str { &self.prefix } /// The entire draw width pub fn width(&self) -> usize { self.draw_target.width() } /// Return the current average time per step pub fn avg_time_per_step(&self) -> Duration { self.est.time_per_step() } /// The expected ETA pub fn eta(&self) -> Duration { if self.len == !0 || self.is_finished() { return Duration::new(0, 0); } let t = duration_to_secs(self.avg_time_per_step()); // add 0.75 to leave 0.25 sec of 0s for the user secs_to_duration(t * self.len.saturating_sub(self.pos) as f64 + 0.75) } /// The expected total duration (that is, elapsed time + expected ETA) pub fn duration(&self) -> Duration { if self.len == !0 || self.is_finished() { return Duration::new(0, 0); } self.started.elapsed() + self.eta() } /// The number of steps per second pub fn per_sec(&self) -> u64 { let avg_time = self.avg_time_per_step().as_nanos(); if avg_time == 0 { 0 } else { (1_000_000_000 / avg_time) as u64 } } /// Call the provided `FnOnce` to update the state. Then redraw the /// progress bar if the state has changed. pub fn update_and_draw(&mut self, f: F) { if self.update(f) { self.draw().ok(); } } /// Call the provided `FnOnce` to update the state. Then unconditionally redraw the /// progress bar. pub fn update_and_force_draw(&mut self, f: F) { self.update(|state| { state.draw_next = state.pos; f(state); }); self.draw().ok(); } /// Call the provided `FnOnce` to update the state. If a draw should be run, returns `true`. pub fn update(&mut self, f: F) -> bool { let old_pos = self.pos; f(self); let new_pos = self.pos; if new_pos != old_pos { self.est.record_step(new_pos); } if new_pos >= self.draw_next { self.draw_next = new_pos.saturating_add(if self.draw_rate != 0 { self.per_sec() / self.draw_rate } else { self.draw_delta }); true } else { false } } /// Finishes the progress bar and leaves the current message. pub fn finish(&mut self) { self.update_and_force_draw(|state| { state.pos = state.len; state.status = Status::DoneVisible; }); } /// Finishes the progress bar at current position and leaves the current message. pub fn finish_at_current_pos(&mut self) { self.update_and_force_draw(|state| { state.status = Status::DoneVisible; }); } /// Finishes the progress bar and sets a message. pub fn finish_with_message(&mut self, msg: impl Into>) { let msg = msg.into(); self.update_and_force_draw(|state| { state.message = msg; state.pos = state.len; state.status = Status::DoneVisible; }); } /// Finishes the progress bar and completely clears it. pub fn finish_and_clear(&mut self) { self.update_and_force_draw(|state| { state.pos = state.len; state.status = Status::DoneHidden; }); } /// Finishes the progress bar and leaves the current message and progress. pub fn abandon(&mut self) { self.update_and_force_draw(|state| { state.status = Status::DoneVisible; }); } /// Finishes the progress bar and sets a message, and leaves the current progress. pub fn abandon_with_message(&mut self, msg: impl Into>) { let msg = msg.into(); self.update_and_force_draw(|state| { state.message = msg; state.status = Status::DoneVisible; }); } /// Finishes the progress bar using the [`ProgressFinish`] behavior stored /// in the [`ProgressStyle`]. pub fn finish_using_style(&mut self) { match self.style.get_on_finish() { ProgressFinish::AndLeave => self.finish(), ProgressFinish::AtCurrentPos => self.finish_at_current_pos(), ProgressFinish::WithMessage(msg) => { // Equivalent to `self.finish_with_message` but avoids borrow checker error self.message.clone_from(msg); self.finish(); } ProgressFinish::AndClear => self.finish_and_clear(), ProgressFinish::Abandon => self.abandon(), ProgressFinish::AbandonWithMessage(msg) => { // Equivalent to `self.abandon_with_message` but avoids borrow checker error self.message.clone_from(msg); self.abandon(); } } } pub(crate) fn draw(&mut self) -> io::Result<()> { // we can bail early if the draw target is hidden. if self.draw_target.is_hidden() { return Ok(()); } let draw_state = ProgressDrawState { lines: if self.should_render() { self.style.format_state(&*self) } else { vec![] }, orphan_lines: 0, finished: self.is_finished(), force_draw: false, move_cursor: false, }; self.draw_target.apply_draw_state(draw_state) } } impl Drop for ProgressState { fn drop(&mut self) { // Progress bar is already finished. Do not need to do anything. if self.is_finished() { return; } self.finish_using_style(); } } pub(crate) struct MultiProgressState { /// The collection of states corresponding to progress bars pub(crate) objects: Vec>, /// Set of `None` elements in the `objects` vector pub(crate) free_set: Vec, /// Indices to the `objects` to maintain correct visual order pub(crate) ordering: Vec, /// Target for draw operation for MultiProgress pub(crate) draw_target: ProgressDrawTarget, /// Whether or not to just move cursor instead of clearing lines pub(crate) move_cursor: bool, } impl MultiProgressState { fn width(&self) -> usize { self.draw_target.width() } pub(crate) fn is_done(&self) -> bool { self.objects.iter().all(|o| match o { Some(obj) => obj.done, None => true, }) } pub(crate) fn remove_idx(&mut self, idx: usize) { if self.objects[idx].take().is_none() { return; } self.free_set.push(idx); self.ordering.retain(|&x| x != idx); } } pub(crate) struct MultiObject { pub(crate) done: bool, pub(crate) draw_state: Option, } /// The drawn state of an element. #[derive(Clone, Debug)] pub(crate) struct ProgressDrawState { /// The lines to print (can contain ANSI codes) pub lines: Vec, /// The number of lines that shouldn't be reaped by the next tick. pub orphan_lines: usize, /// True if the bar no longer needs drawing. pub finished: bool, /// True if drawing should be forced. pub force_draw: bool, /// True if we should move the cursor up when possible instead of clearing lines. pub move_cursor: bool, } impl ProgressDrawState { pub fn draw_to_term(&self, term: &Term) -> io::Result<()> { for line in &self.lines { term.write_line(line)?; } Ok(()) } } #[derive(Debug)] pub(crate) enum Status { InProgress, DoneVisible, DoneHidden, } /// Target for draw operations /// /// This tells a progress bar or a multi progress object where to paint to. /// The draw target is a stateful wrapper over a drawing destination and /// internally optimizes how often the state is painted to the output /// device. pub struct ProgressDrawTarget { pub(crate) kind: ProgressDrawTargetKind, } impl ProgressDrawTarget { /// Draw to a buffered stdout terminal at a max of 15 times a second. /// /// For more information see `ProgressDrawTarget::to_term`. pub fn stdout() -> ProgressDrawTarget { ProgressDrawTarget::term(Term::buffered_stdout(), 15) } /// Draw to a buffered stderr terminal at a max of 15 times a second. /// /// This is the default draw target for progress bars. For more /// information see `ProgressDrawTarget::to_term`. pub fn stderr() -> ProgressDrawTarget { ProgressDrawTarget::term(Term::buffered_stderr(), 15) } /// Draw to a buffered stdout terminal at a max of `refresh_rate` times a second. /// /// For more information see `ProgressDrawTarget::to_term`. pub fn stdout_with_hz(refresh_rate: u64) -> ProgressDrawTarget { ProgressDrawTarget::term(Term::buffered_stdout(), refresh_rate) } /// Draw to a buffered stderr terminal at a max of `refresh_rate` times a second. /// /// For more information see `ProgressDrawTarget::to_term`. pub fn stderr_with_hz(refresh_rate: u64) -> ProgressDrawTarget { ProgressDrawTarget::term(Term::buffered_stderr(), refresh_rate) } /// Draw to a buffered stdout terminal without max framerate. /// /// This is useful when data is known to come in very slowly and /// not rendering some updates would be a problem (for instance /// when messages are used extensively). /// /// For more information see `ProgressDrawTarget::to_term`. pub fn stdout_nohz() -> ProgressDrawTarget { ProgressDrawTarget::term(Term::buffered_stdout(), None) } /// Draw to a buffered stderr terminal without max framerate. /// /// This is useful when data is known to come in very slowly and /// not rendering some updates would be a problem (for instance /// when messages are used extensively). /// /// For more information see `ProgressDrawTarget::to_term`. pub fn stderr_nohz() -> ProgressDrawTarget { ProgressDrawTarget::term(Term::buffered_stderr(), None) } /// Draw to a terminal, optionally with a specific refresh rate. /// /// Progress bars are by default drawn to terminals however if the /// terminal is not user attended the entire progress bar will be /// hidden. This is done so that piping to a file will not produce /// useless escape codes in that file. /// /// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead. #[allow(clippy::wrong_self_convention)] #[deprecated(since = "0.16.0", note = "Use `ProgressDrawTarget::term` instead")] pub fn to_term(term: Term, refresh_rate: impl Into>) -> ProgressDrawTarget { ProgressDrawTarget::term(term, refresh_rate) } /// Draw to a terminal, optionally with a specific refresh rate. /// /// Progress bars are by default drawn to terminals however if the /// terminal is not user attended the entire progress bar will be /// hidden. This is done so that piping to a file will not produce /// useless escape codes in that file. /// /// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead. pub fn term(term: Term, refresh_rate: impl Into>) -> ProgressDrawTarget { let rate = refresh_rate .into() .map(|x| Duration::from_millis(1000 / x)) .unwrap_or_else(|| Duration::from_secs(0)); ProgressDrawTarget { kind: ProgressDrawTargetKind::Term { term, last_line_count: 0, rate, last_draw: Instant::now() - rate, }, } } /// A hidden draw target. /// /// This forces a progress bar to be not rendered at all. pub fn hidden() -> ProgressDrawTarget { ProgressDrawTarget { kind: ProgressDrawTargetKind::Hidden, } } /// Returns true if the draw target is hidden. /// /// This is internally used in progress bars to figure out if overhead /// from drawing can be prevented. pub fn is_hidden(&self) -> bool { match self.kind { ProgressDrawTargetKind::Hidden => true, ProgressDrawTargetKind::Term { ref term, .. } => !term.is_term(), _ => false, } } /// Returns the current width of the draw target. fn width(&self) -> usize { match self.kind { ProgressDrawTargetKind::Term { ref term, .. } => term.size().1 as usize, ProgressDrawTargetKind::Remote { ref state, .. } => state.read().unwrap().width(), ProgressDrawTargetKind::Hidden => unreachable!(), } } /// Apply the given draw state (draws it). pub(crate) fn apply_draw_state(&mut self, draw_state: ProgressDrawState) -> io::Result<()> { let (term, last_line_count, last_draw) = match self.kind { ProgressDrawTargetKind::Term { ref term, ref mut last_line_count, rate, ref mut last_draw, } if draw_state.finished || draw_state.force_draw || last_draw.elapsed() > rate => { (term, last_line_count, last_draw) } ProgressDrawTargetKind::Remote { idx, ref chan, .. } => { return chan .lock() .unwrap() .send((idx, draw_state)) .map_err(|e| io::Error::new(io::ErrorKind::Other, e)); } // Hidden, finished, or no need to refresh yet _ => return Ok(()), }; if !draw_state.lines.is_empty() && draw_state.move_cursor { term.move_cursor_up(*last_line_count)?; } else { term.clear_last_lines(*last_line_count)?; } draw_state.draw_to_term(term)?; term.flush()?; *last_line_count = draw_state.lines.len() - draw_state.orphan_lines; *last_draw = Instant::now(); Ok(()) } /// Properly disconnects from the draw target pub(crate) fn disconnect(&self) { match self.kind { ProgressDrawTargetKind::Term { .. } => {} ProgressDrawTargetKind::Remote { idx, ref chan, .. } => { chan.lock() .unwrap() .send(( idx, ProgressDrawState { lines: vec![], orphan_lines: 0, finished: true, force_draw: false, move_cursor: false, }, )) .ok(); } ProgressDrawTargetKind::Hidden => {} }; } } pub(crate) enum ProgressDrawTargetKind { Term { term: Term, last_line_count: usize, rate: Duration, last_draw: Instant, }, Remote { state: Arc>, idx: usize, chan: Mutex>, }, Hidden, } indicatif-0.16.0/src/style.rs000064400000000000000000000313000000000000000141110ustar 00000000000000use console::{measure_text_width, Style}; use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanDuration}; use crate::state::ProgressState; use crate::utils::{expand_template, pad_str}; use std::borrow::Cow; #[cfg(feature = "improved_unicode")] use unicode_segmentation::UnicodeSegmentation; /// Behavior of a progress bar when it is finished. This is invoked when a /// [`ProgressBar`] or [`ProgresBarIter`](crate::ProgressBarIter) completes and /// [`ProgressBar::is_finished()`] is false. #[derive(Clone, Debug)] pub enum ProgressFinish { /// Finishes the progress bar and leaves the current message. /// Same behavior as calling [`ProgressBar::finish()`]. AndLeave, /// Finishes the progress bar at current position and leaves the current message. /// Same behavior as calling [`ProgressBar::finish_at_current_pos()`]. AtCurrentPos, /// Finishes the progress bar and sets a message. /// Same behavior as calling [`ProgressBar::finish_with_message()`]. WithMessage(Cow<'static, str>), /// Finishes the progress bar and completely clears it. This is the default behavior. /// Same behavior as calling [`ProgressBar::finish_and_clear()`]. AndClear, /// Finishes the progress bar and leaves the current message and progress. /// Same behavior as calling [`ProgressBar::abandon()`]. Abandon, /// Finishes the progress bar and sets a message, and leaves the current progress. /// Same behavior as calling [`ProgressBar::abandon_with_message()`]. AbandonWithMessage(Cow<'static, str>), } impl Default for ProgressFinish { fn default() -> Self { Self::AndClear } } /// Controls the rendering style of progress bars. #[derive(Clone, Debug)] pub struct ProgressStyle { tick_strings: Vec>, progress_chars: Vec>, template: Box, on_finish: ProgressFinish, // how unicode-big each char in progress_chars is char_width: usize, } #[cfg(feature = "improved_unicode")] fn segment(s: &str) -> Vec> { UnicodeSegmentation::graphemes(s, true) .map(|s| s.into()) .collect() } #[cfg(not(feature = "improved_unicode"))] fn segment(s: &str) -> Vec> { s.chars().map(|x| x.to_string().into()).collect() } #[cfg(feature = "improved_unicode")] fn measure(s: &str) -> usize { unicode_width::UnicodeWidthStr::width(s) } #[cfg(not(feature = "improved_unicode"))] fn measure(s: &str) -> usize { s.chars().count() } /// finds the unicode-aware width of the passed grapheme cluters /// panics on an empty parameter, or if the characters are not equal-width fn width(c: &[Box]) -> usize { c.iter() .map(|s| measure(s.as_ref())) .fold(None, |acc, new| { match acc { None => return Some(new), Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"), } acc }) .unwrap() } impl ProgressStyle { /// Returns the default progress bar style for bars. pub fn default_bar() -> ProgressStyle { let progress_chars = segment("█░"); let char_width = width(&progress_chars); ProgressStyle { tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ " .chars() .map(|c| c.to_string().into()) .collect(), progress_chars, char_width, template: "{wide_bar} {pos}/{len}".into(), on_finish: ProgressFinish::default(), } } /// Returns the default progress bar style for spinners. pub fn default_spinner() -> ProgressStyle { let progress_chars = segment("█░"); let char_width = width(&progress_chars); ProgressStyle { tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ " .chars() .map(|c| c.to_string().into()) .collect(), progress_chars, char_width, template: "{spinner} {msg}".into(), on_finish: ProgressFinish::default(), } } /// Sets the tick character sequence for spinners. pub fn tick_chars(mut self, s: &str) -> ProgressStyle { self.tick_strings = s.chars().map(|c| c.to_string().into()).collect(); // Format bar will panic with some potentially confusing message, better to panic here // with a message explicitly informing of the problem assert!( self.tick_strings.len() >= 2, "at least 2 tick chars required" ); self } /// Sets the tick string sequence for spinners. pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle { self.tick_strings = s.iter().map(|s| s.to_string().into()).collect(); // Format bar will panic with some potentially confusing message, better to panic here // with a message explicitly informing of the problem assert!( self.progress_chars.len() >= 2, "at least 2 tick strings required" ); self } /// Sets the progress characters `(filled, current, to do)`. /// You can pass more then three for a more detailed display. /// All passed grapheme clusters need to be of equal width. pub fn progress_chars(mut self, s: &str) -> ProgressStyle { self.progress_chars = segment(s); // Format bar will panic with some potentially confusing message, better to panic here // with a message explicitly informing of the problem assert!( self.progress_chars.len() >= 2, "at least 2 progress chars required" ); self.char_width = width(&self.progress_chars); self } /// Sets the template string for the progress bar. /// /// List of keys is available at crate root docs. pub fn template(mut self, s: &str) -> ProgressStyle { self.template = s.into(); self } /// Sets the finish behavior for the progress bar. /// This behavior is invoked when [`ProgressBar`] or /// [`ProgresBarIter`](crate::ProgressBarIter) completes and /// [`ProgressBar::is_finished()`] is false. /// If you don't want the progress bar to be automatically finished then /// call `on_finish(None)`. pub fn on_finish(mut self, finish: ProgressFinish) -> ProgressStyle { self.on_finish = finish; self } /// Returns the tick char for a given number. #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_tick_str")] pub fn get_tick_char(&self, idx: u64) -> char { self.get_tick_str(idx).chars().next().unwrap_or(' ') } /// Returns the tick string for a given number. pub fn get_tick_str(&self, idx: u64) -> &str { &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)] } /// Returns the tick char for the finished state. #[deprecated(since = "0.13.0", note = "Deprecated in favor of get_final_tick_str")] pub fn get_final_tick_char(&self) -> char { self.get_final_tick_str().chars().next().unwrap_or(' ') } /// Returns the tick string for the finished state. pub fn get_final_tick_str(&self) -> &str { &self.tick_strings[self.tick_strings.len() - 1] } /// Returns the finish behavior. pub fn get_on_finish(&self) -> &ProgressFinish { &self.on_finish } pub(crate) fn format_bar(&self, fract: f32, width: usize, alt_style: Option<&Style>) -> String { // The number of clusters from progress_chars to write (rounding down). let width = width / self.char_width; // The number of full clusters (including a fractional component for a partially-full one). let fill = fract * width as f32; // The number of entirely full clusters (by truncating `fill`). let entirely_filled = fill as usize; // 1 if the bar is not entirely empty or full (meaning we need to draw the "current" // character between the filled and "to do" segment), 0 otherwise. let head = if fill > 0.0 && entirely_filled < width { 1 } else { 0 }; let pb = self.progress_chars[0].repeat(entirely_filled); let cur = if head == 1 { // Number of fine-grained progress entries in progress_chars. let n = self.progress_chars.len().saturating_sub(2); let cur_char = if n <= 1 { // No fine-grained entries. 1 is the single "current" entry if we have one, the "to // do" entry if not. 1 } else { // Pick a fine-grained entry, ranging from the last one (n) if the fractional part // of fill is 0 to the first one (1) if the fractional part of fill is almost 1. n.saturating_sub((fill.fract() * n as f32) as usize) }; self.progress_chars[cur_char].to_string() } else { "".into() }; // Number of entirely empty clusters needed to fill the bar up to `width`. let bg = width.saturating_sub(entirely_filled).saturating_sub(head); let rest = self.progress_chars[self.progress_chars.len() - 1].repeat(bg); format!( "{}{}{}", pb, cur, alt_style.unwrap_or(&Style::new()).apply_to(rest) ) } pub(crate) fn format_state(&self, state: &ProgressState) -> Vec { let (pos, len) = state.position(); let mut rv = vec![]; for line in self.template.lines() { let mut wide_element = None; let s = expand_template(line, |var| match var.key { "wide_bar" => { wide_element = Some(var.duplicate_for_key("bar")); "\x00".into() } "bar" => self.format_bar( state.fraction(), var.width.unwrap_or(20), var.alt_style.as_ref(), ), "spinner" => state.current_tick_str().to_string(), "wide_msg" => { wide_element = Some(var.duplicate_for_key("msg")); "\x00".into() } "msg" => state.message().to_string(), "prefix" => state.prefix().to_string(), "pos" => pos.to_string(), "len" => len.to_string(), "percent" => format!("{:.*}", 0, state.fraction() * 100f32), "bytes" => format!("{}", HumanBytes(state.pos)), "total_bytes" => format!("{}", HumanBytes(state.len)), "decimal_bytes" => format!("{}", DecimalBytes(state.pos)), "decimal_total_bytes" => format!("{}", DecimalBytes(state.len)), "binary_bytes" => format!("{}", BinaryBytes(state.pos)), "binary_total_bytes" => format!("{}", BinaryBytes(state.len)), "elapsed_precise" => format!("{}", FormattedDuration(state.started.elapsed())), "elapsed" => format!("{:#}", HumanDuration(state.started.elapsed())), "per_sec" => format!("{}/s", state.per_sec()), "bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec())), "binary_bytes_per_sec" => format!("{}/s", BinaryBytes(state.per_sec())), "eta_precise" => format!("{}", FormattedDuration(state.eta())), "eta" => format!("{:#}", HumanDuration(state.eta())), "duration_precise" => format!("{}", FormattedDuration(state.duration())), "duration" => format!("{:#}", HumanDuration(state.duration())), _ => "".into(), }); rv.push(if let Some(ref var) = wide_element { let total_width = state.width(); if var.key == "bar" { let bar_width = total_width.saturating_sub(measure_text_width(&s)); s.replace( "\x00", &self.format_bar(state.fraction(), bar_width, var.alt_style.as_ref()), ) } else if var.key == "msg" { let msg_width = total_width.saturating_sub(measure_text_width(&s)); let msg = pad_str(state.message(), msg_width, var.align, true); s.replace( "\x00", if var.last_element { msg.trim_end() } else { &msg }, ) } else { unreachable!() } } else { s.to_string() }); } rv } } indicatif-0.16.0/src/utils.rs000064400000000000000000000231150000000000000141160ustar 00000000000000use std::borrow::Cow; use std::fmt; use std::time::{Duration, Instant}; use regex::{Captures, Regex}; use console::{measure_text_width, Style}; pub fn duration_to_secs(d: Duration) -> f64 { d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64 } pub fn secs_to_duration(s: f64) -> Duration { let secs = s.trunc() as u64; let nanos = (s.fract() * 1_000_000_000f64) as u32; Duration::new(secs, nanos) } /// Ring buffer with constant capacity. Used by `ProgressBar`s to display `{eta}`, `{eta_precise}`, /// and `{*_per_sec}`. pub struct Estimate { buf: Box<[f64; 15]>, /// Lower 4 bits signify the current length, meaning how many values of `buf` are actually /// meaningful (and not just set to 0 by initialization). /// /// The upper 4 bits signify the last used index in the `buf`. The estimate is currently /// implemented as a ring buffer and when recording a new step the oldest value is overwritten. /// Last index is the most recently used position, and as elements are always stored with /// insertion order, `last_index + 1` is the least recently used position and is the first /// to be overwritten. data: u8, start_time: Instant, start_value: u64, } impl Estimate { fn len(&self) -> u8 { self.data & 0x0F } fn set_len(&mut self, len: u8) { // Sanity check to make sure math is correct as otherwise it could result in unexpected bugs debug_assert!(len < 16); self.data = (self.data & 0xF0) | len; } fn last_idx(&self) -> u8 { (self.data & 0xF0) >> 4 } fn set_last_idx(&mut self, last_idx: u8) { // This will wrap last_idx on overflow (setting to 16 will result in 0); this is fine // because Estimate::buf is 15 elements long and this is a ring buffer, so overwriting // the oldest value is correct self.data = ((last_idx & 0x0F) << 4) | (self.data & 0x0F); } pub fn new() -> Self { let this = Self { buf: Box::new([0.0; 15]), data: 0, start_time: Instant::now(), start_value: 0, }; // Make sure not to break anything accidentally as self.data can't handle bufs longer than // 15 elements (not enough space in a u8) debug_assert!(this.buf.len() < 16); this } pub fn reset(&mut self, start_value: u64) { self.start_time = Instant::now(); self.start_value = start_value; self.data = 0; } pub fn record_step(&mut self, value: u64) { let item = { let divisor = value.saturating_sub(self.start_value) as f64; if divisor == 0.0 { 0.0 } else { duration_to_secs(self.start_time.elapsed()) / divisor } }; self.push(item); } /// Adds the `value` into the buffer, overwriting the oldest one if full, or increasing length /// by 1 and appending otherwise. fn push(&mut self, value: f64) { let len = self.len(); let last_idx = self.last_idx(); if self.buf.len() > usize::from(len) { // Buffer isn't yet full, increase it's size self.set_len(len + 1); self.buf[usize::from(last_idx)] = value; } else { // Buffer is full, overwrite the oldest value let idx = last_idx % len; self.buf[usize::from(idx)] = value; } self.set_last_idx(last_idx + 1); } pub fn time_per_step(&self) -> Duration { let len = self.len(); secs_to_duration(self.buf[0..usize::from(len)].iter().sum::() / f64::from(len)) } } impl fmt::Debug for Estimate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Estimate") .field("buf", &self.buf) .field("len", &self.len()) .field("last_idx", &self.last_idx()) .field("start_time", &self.start_time) .field("start_value", &self.start_value) .finish() } } #[derive(PartialEq, Eq, Debug, Copy, Clone)] pub enum Alignment { Left, Center, Right, } #[derive(Debug)] pub struct TemplateVar<'a> { pub key: &'a str, pub align: Alignment, pub truncate: bool, pub width: Option, pub style: Option