pbr-1.1.1/.cargo_vcs_info.json0000644000000001360000000000100116170ustar { "git": { "sha1": "87db6b8dbdcb9347ed945a1bbfb1d0f30b23ab18" }, "path_in_vcs": "" }pbr-1.1.1/.github/workflows/ci.yml000064400000000000000000000021561046102023000151260ustar 00000000000000name: CI on: pull_request: push: jobs: test: name: Test strategy: matrix: os: ["ubuntu-latest", "windows-latest", "macos-latest"] rust: ["stable", "1.58"] runs-on: ${{ matrix.os }} steps: - name: Checkout repository uses: actions/checkout@v3 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Build run: cargo build - name: Build examples run: cargo build --examples - name: Install faketty if: matrix.rust == 'stable' && matrix.os != 'windows-latest' run: | cargo install faketty echo "fake_tty=faketty" >> $GITHUB_ENV - name: Test if: env.fake_tty || matrix.os == 'windows-latest' run: ${{ env.fake_tty }} cargo test --workspace fmt: name: rustfmt runs-on: ubuntu-latest steps: - name: Checkout source uses: actions/checkout@v3 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Run rustfmt check run: cargo fmt --check pbr-1.1.1/.gitignore000064400000000000000000000000331046102023000123730ustar 00000000000000/target /Cargo.lock draft* pbr-1.1.1/Cargo.lock0000644000000061000000000000100075670ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "crossbeam-channel" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] [[package]] name = "getrandom" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "pbr" version = "1.1.1" dependencies = [ "crossbeam-channel", "libc", "rand", "winapi", ] [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[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" pbr-1.1.1/Cargo.toml0000644000000022170000000000100076170ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "pbr" version = "1.1.1" authors = [ "Ariel Mashraki ", "Steven Fackler ", ] exclude = ["gif/"] description = "Console progress bar for Rust" documentation = "https://a8m.github.io/pb/doc/pbr/index.html" readme = "README.md" keywords = [ "cli", "progress", "terminal", "pb", ] license = "MIT" repository = "https://github.com/a8m/pb" [dependencies.crossbeam-channel] version = "0.5" [dependencies.libc] version = "0.2" [dev-dependencies.rand] version = "0.8" [target."cfg(target_os = \"windows\")".dependencies.winapi] version = "0.3" features = [ "wincon", "processenv", "winbase", ] pbr-1.1.1/Cargo.toml.orig000064400000000000000000000011201046102023000132700ustar 00000000000000[package] name = "pbr" version = "1.1.1" authors = ["Ariel Mashraki ", "Steven Fackler "] edition = "2018" description = "Console progress bar for Rust" documentation = "https://a8m.github.io/pb/doc/pbr/index.html" repository = "https://github.com/a8m/pb" exclude = ["gif/"] keywords = ["cli", "progress", "terminal", "pb"] license = "MIT" [dependencies] libc = "0.2" crossbeam-channel = "0.5" [target.'cfg(target_os = "windows")'.dependencies.winapi] version = "0.3" features = ["wincon", "processenv", "winbase"] [dev-dependencies] rand = "0.8" pbr-1.1.1/LICENSE.md000064400000000000000000000021101046102023000120050ustar 00000000000000# MIT License Copyright (c) 2015-2016 Ariel Mashraki and contributors 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. pbr-1.1.1/README.md000064400000000000000000000053211046102023000116670ustar 00000000000000# Terminal progress bar for Rust [![Latest version](https://img.shields.io/crates/v/pbr.svg)](https://crates.io/crates/pbr) [![License](https://img.shields.io/crates/l/pbr.svg)](https://github.com/a8m/pb/blob/master/LICENSE.md) [![Docs](https://img.shields.io/badge/docs-reference-blue.svg)](https://a8m.github.io/pb/doc/pbr/index.html) [![Build Status](https://travis-ci.org/a8m/pb.svg?branch=master)](https://travis-ci.org/a8m/pb) [![Gitter](https://badges.gitter.im/a8m/pb.svg)](https://gitter.im/a8m/pb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Console progress bar for Rust Inspired from [pb](http://github.com/cheggaaa/pb), support and tested on MacOS, Linux and Windows ![Screenshot](https://github.com/a8m/pb/blob/master/gif/rec_v3.gif) [Documentation](https://a8m.github.io/pb/doc/pbr/index.html) ### Examples 1. simple example ```rust use pbr::ProgressBar; use std::thread; fn main() { let count = 1000; let mut pb = ProgressBar::new(count); pb.format("╢▌▌░╟"); for _ in 0..count { pb.inc(); thread::sleep_ms(200); } pb.finish_print("done"); } ``` 2. MultiBar example. see full example [here](https://github.com/a8m/pb/blob/master/examples/multi.rs) ```rust use std::thread; use pbr::MultiBar; use std::time::Duration; fn main() { let mut mb = MultiBar::new(); let count = 100; mb.println("Application header:"); let mut p1 = mb.create_bar(count); let _ = thread::spawn(move || { for _ in 0..count { p1.inc(); thread::sleep(Duration::from_millis(100)); } // notify the multibar that this bar finished. p1.finish(); }); mb.println("add a separator between the two bars"); let mut p2 = mb.create_bar(count * 2); let _ = thread::spawn(move || { for _ in 0..count * 2 { p2.inc(); thread::sleep(Duration::from_millis(100)); } // notify the multibar that this bar finished. p2.finish(); }); // start listen to all bars changes. // this is a blocking operation, until all bars will finish. // to ignore blocking, you can run it in a different thread. mb.listen(); } ``` 3. Broadcast writing (simple file copying) ```rust #![feature(io)] use std::io::copy; use std::io::prelude::*; use std::fs::File; use pbr::{ProgressBar, Units}; fn main() { let mut file = File::open("/usr/share/dict/words").unwrap(); let n_bytes = file.metadata().unwrap().len() as usize; let mut pb = ProgressBar::new(n_bytes); pb.set_units(Units::Bytes); let mut handle = File::create("copy-words").unwrap().broadcast(&mut pb); copy(&mut file, &mut handle).unwrap(); pb.finish_print("done"); } ``` ### License MIT pbr-1.1.1/examples/max_refresh_rate.rs000064400000000000000000000004601046102023000161110ustar 00000000000000use pbr::ProgressBar; use std::thread; use std::time::Duration; fn main() { let mut pb = ProgressBar::new(10000); pb.set_max_refresh_rate(Some(Duration::from_secs(1))); for _ in 0..10000 { pb.inc(); thread::sleep(Duration::from_millis(1)); } pb.finish_print(""); } pbr-1.1.1/examples/multi.rs000064400000000000000000000036571046102023000137400ustar 00000000000000use pbr::MultiBar; use rand::prelude::*; use std::thread; use std::time::Duration; fn main() { let mb = MultiBar::new(); mb.println("Your Application Header:"); mb.println(""); for i in 1..6 { let count = 100 * i; let mut pb = mb.create_bar(count); pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏"); pb.show_message = true; thread::spawn(move || { for _ in 0..count / 20 { for _ in 0..20 { pb.message("Waiting : "); thread::sleep(Duration::from_millis(50)); pb.tick(); } for _ in 0..20 { let n = thread_rng().gen_range(0..100); pb.message("Connected: "); thread::sleep(Duration::from_millis(n)); pb.inc(); } } for _ in 0..20 { pb.message("Cleaning :"); thread::sleep(Duration::from_millis(100)); pb.tick(); } pb.finish_print(&format!("{}: Pull complete", rand_string())); }); } mb.println(""); mb.println("Text lines separate between two sections: "); mb.println(""); for i in 1..4 { let count = 100 * i; let mut pb = mb.create_bar(count); thread::spawn(move || { for _ in 0..count { pb.inc(); let n = thread_rng().gen_range(0..100); thread::sleep(Duration::from_millis(n)); } pb.finish(); }); } mb.listen(); println!("\nall bars done!\n"); } fn rand_string() -> String { let mut v = Vec::new(); while v.len() < 12 { let b = random::(); // [0-9a-f] if b > 47 && b < 58 || b > 96 && b < 103 { v.push(b); } } std::str::from_utf8(&v).unwrap().to_string() } pbr-1.1.1/examples/multi_bg.rs000064400000000000000000000024011046102023000143720ustar 00000000000000use pbr::MultiBar; use std::{ sync::{ atomic::{AtomicBool, Ordering}, Arc, }, thread, time::Duration, }; fn main() { let complete = Arc::new(AtomicBool::new(false)); let progress = Arc::new(MultiBar::new()); thread::spawn({ let complete = Arc::clone(&complete); let progress = Arc::clone(&progress); move || { for task in 1..=10 { thread::spawn({ let progress = Arc::clone(&progress); move || { let mut bar = progress.create_bar(100); bar.message(&format!("Task {}: ", task)); for _ in 0..100 { thread::sleep(Duration::from_millis(50)); bar.inc(); } bar.finish_print(&format!("Task {} Complete", task)); } }); thread::sleep(Duration::from_millis(1000)); } complete.store(true, Ordering::SeqCst); } }); while !complete.load(Ordering::SeqCst) { let _ = progress.listen(); thread::sleep(Duration::from_millis(1000)); } let _ = progress.listen(); } pbr-1.1.1/examples/npm_bar.rs000064400000000000000000000013351046102023000142130ustar 00000000000000use pbr::ProgressBar; use std::thread; use std::time::Duration; fn main() { let count = 30; let mut pb = ProgressBar::new(count * 10); pb.tick_format("\\|/-"); pb.format("|#--|"); pb.show_tick = true; pb.show_speed = false; pb.show_percent = false; pb.show_counter = false; pb.show_time_left = false; pb.inc(); for _ in 0..count { for _ in 0..10 { pb.message("normalize -> thing "); thread::sleep(Duration::from_millis(80)); pb.tick(); } for _ in 0..10 { pb.message("fuzz -> tree "); thread::sleep(Duration::from_millis(80)); pb.inc(); } } pb.finish_println("done!"); } pbr-1.1.1/examples/simple.rs000064400000000000000000000005671046102023000140740ustar 00000000000000use pbr::ProgressBar; use rand::prelude::*; use std::thread; use std::time::Duration; fn main() { let count = 500; let mut pb = ProgressBar::new(count); pb.format("╢▌▌░╟"); for _ in 0..count { pb.inc(); let n = thread_rng().gen_range(0..100); thread::sleep(Duration::from_millis(n)); } pb.finish_println("done!"); } pbr-1.1.1/src/lib.rs000064400000000000000000000076561046102023000123300ustar 00000000000000//! # Terminal progress bar for Rust //! //! Console progress bar for Rust Inspired from [pb](http://github.com/cheggaaa/pb), support and //! tested on MacOS, Linux and Windows //! //! ![Screenshot](https://raw.githubusercontent.com/a8m/pb/master/gif/rec_v3.gif) //! //! [Documentation](http://a8m.github.io/pb/doc/pbr/index.html) //! //! ### Examples //! 1. simple example //! //! ```ignore //! use pbr::ProgressBar; //! use std::thread; //! //! fn main() { //! let count = 1000; //! let mut pb = ProgressBar::new(count); //! pb.format("╢▌▌░╟"); //! for _ in 0..count { //! pb.inc(); //! thread::sleep_ms(200); //! } //! pb.finish_print("done"); //! } //! ``` //! //! 2. MultiBar example. see full example [here](https://github.com/a8m/pb/blob/master/examples/multi.rs) //! //! ```ignore //! use std::thread; //! use pbr::MultiBar; //! use std::time::Duration; //! //! fn main() { //! let mut mb = MultiBar::new(); //! let count = 100; //! mb.println("Application header:"); //! //! let mut p1 = mb.create_bar(count); //! let _ = thread::spawn(move || { //! for _ in 0..count { //! p1.inc(); //! thread::sleep(Duration::from_millis(100)); //! } //! // notify the multibar that this bar finished. //! p1.finish(); //! }); //! //! mb.println("add a separator between the two bars"); //! //! let mut p2 = mb.create_bar(count * 2); //! let _ = thread::spawn(move || { //! for _ in 0..count * 2 { //! p2.inc(); //! thread::sleep(Duration::from_millis(100)); //! } //! // notify the multibar that this bar finished. //! p2.finish(); //! }); //! //! // start listen to all bars changes. //! // this is a blocking operation, until all bars will finish. //! // to ignore blocking, you can run it in a different thread. //! mb.listen(); //! } //! ``` //! //! 3. Broadcast writing(simple file copying) //! //! ```ignore //! #![feature(io)] //! use std::io::copy; //! use std::io::prelude::*; //! use std::fs::File; //! use pbr::{ProgressBar, Units}; //! //! fn main() { //! let mut file = File::open("/usr/share/dict/words").unwrap(); //! let n_bytes = file.metadata().unwrap().len() as usize; //! let mut pb = ProgressBar::new(n_bytes); //! pb.set_units(Units::Bytes); //! let mut handle = File::create("copy-words").unwrap().broadcast(&mut pb); //! copy(&mut file, &mut handle).unwrap(); //! pb.finish_print("done"); //! } //! ``` // Macro for writing to the giving writer. // Used in both pb.rs and multi.rs modules. // // # Examples // // ``` // let w = io::stdout(); // printfl!(w, ""); // printfl!(w, "\r{}", out); // // ``` macro_rules! printfl { ($w:expr, $($tt:tt)*) => {{ $w.write_all(&format!($($tt)*).as_bytes()).ok().expect("write() fail"); $w.flush().ok().expect("flush() fail"); }} } mod multi; mod pb; mod tty; pub use multi::{MultiBar, Pipe}; pub use pb::{ProgressBar, Units}; use std::io::{stdout, Stdout, Write}; pub struct PbIter where I: Iterator, T: Write, { iter: I, progress_bar: ProgressBar, } impl PbIter where I: Iterator, { pub fn new(iter: I) -> Self { Self::on(stdout(), iter) } } impl PbIter where I: Iterator, T: Write, { pub fn on(handle: T, iter: I) -> Self { let size = iter.size_hint().0; PbIter { iter, progress_bar: ProgressBar::on(handle, size as u64), } } } impl Iterator for PbIter where I: Iterator, T: Write, { type Item = I::Item; fn next(&mut self) -> Option { match self.iter.next() { Some(i) => { self.progress_bar.inc(); Some(i) } None => None, } } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } pbr-1.1.1/src/multi.rs000064400000000000000000000154731046102023000127100ustar 00000000000000use crate::tty::move_cursor_up; use crate::ProgressBar; use crossbeam_channel::{unbounded, Receiver, Sender}; use std::io::{Result, Stdout, Write}; use std::str::from_utf8; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; pub struct MultiBar { state: Mutex>, chan: (Sender, Receiver), nbars: AtomicUsize, } struct State { lines: Vec, nlines: usize, handle: T, } impl MultiBar { /// Create a new MultiBar with stdout as a writer. /// /// # Examples /// /// ```no_run /// use std::thread; /// use pbr::MultiBar; /// use std::time::Duration; /// /// let mut mb = MultiBar::new(); /// mb.println("Application header:"); /// /// # let count = 250; /// let mut p1 = mb.create_bar(count); /// let _ = thread::spawn(move || { /// for _ in 0..count { /// p1.inc(); /// thread::sleep(Duration::from_millis(100)); /// } /// // notify the multibar that this bar finished. /// p1.finish(); /// }); /// /// mb.println("add a separator between the two bars"); /// /// let mut p2 = mb.create_bar(count * 2); /// let _ = thread::spawn(move || { /// for _ in 0..count * 2 { /// p2.inc(); /// thread::sleep(Duration::from_millis(100)); /// } /// // notify the multibar that this bar finished. /// p2.finish(); /// }); /// /// // start listen to all bars changes. /// // this is a blocking operation, until all bars will finish. /// // to ignore blocking, you can run it in a different thread. /// mb.listen(); /// ``` pub fn new() -> MultiBar { MultiBar::on(::std::io::stdout()) } } impl MultiBar { /// Create a new MultiBar with an arbitrary writer. /// /// # Examples /// /// ```no_run /// use pbr::MultiBar; /// use std::io::stderr; /// /// let mut mb = MultiBar::on(stderr()); /// // ... /// // see full example in `MultiBar::new` /// // ... /// ``` pub fn on(handle: T) -> MultiBar { MultiBar { state: Mutex::new(State { lines: Vec::new(), handle, nlines: 0, }), chan: unbounded(), nbars: AtomicUsize::new(0), } } /// println used to add text lines between the bars. /// for example: you could add a header to your application, /// or text separators between bars. /// /// # Examples /// /// ```no_run /// use pbr::MultiBar; /// /// let mut mb = MultiBar::new(); /// mb.println("Application header:"); /// /// # let count = 250; /// let mut p1 = mb.create_bar(count); /// // ... /// /// mb.println("Text line between bar1 and bar2"); /// /// let mut p2 = mb.create_bar(count); /// // ... /// /// mb.println("Text line between bar2 and bar3"); /// /// // ... /// // ... /// mb.listen(); /// ``` pub fn println(&self, s: &str) { let mut state = self.state.lock().unwrap(); state.lines.push(s.to_owned()); state.nlines += 1; } /// create_bar creates new `ProgressBar` with `Pipe` as the writer. /// /// The ordering of the method calls is important. it means that in /// the first call, you get a progress bar in level 1, in the 2nd call, /// you get a progress bar in level 2, and so on. /// /// ProgressBar that finish its work, must call `finish()` (or `finish_print`) /// to notify the `MultiBar` about it. /// /// # Examples /// /// ```no_run /// use pbr::MultiBar; /// /// let mut mb = MultiBar::new(); /// # let (count1, count2, count3) = (250, 62500, 15625000); /// /// // progress bar in level 1 /// let mut p1 = mb.create_bar(count1); /// // ... /// /// // progress bar in level 2 /// let mut p2 = mb.create_bar(count2); /// // ... /// /// // progress bar in level 3 /// let mut p3 = mb.create_bar(count3); /// /// // ... /// mb.listen(); /// ``` pub fn create_bar(&self, total: u64) -> ProgressBar { let mut state = self.state.lock().unwrap(); state.lines.push(String::new()); state.nlines += 1; self.nbars.fetch_add(1, Ordering::SeqCst); let mut p = ProgressBar::on( Pipe { level: state.nlines - 1, chan: self.chan.0.clone(), }, total, ); p.is_multibar = true; p.add(0); p } /// listen start listen to all bars changes. /// /// `ProgressBar` that finish its work, must call `finish()` (or `finish_print`) /// to notify the `MultiBar` about it. /// /// This is a blocking operation and blocks until all bars will /// finish. /// To ignore blocking, you can run it in a different thread. /// /// # Examples /// /// ```no_run /// use std::thread; /// use pbr::MultiBar; /// /// let mut mb = MultiBar::new(); /// /// // ... /// // create some bars here /// // ... /// /// thread::spawn(move || { /// mb.listen(); /// println!("all bars done!"); /// }); /// /// // ... /// ``` pub fn listen(&self) { let mut first = true; let mut out = String::new(); while self.nbars.load(Ordering::SeqCst) > 0 { // receive message let msg = self.chan.1.recv().unwrap(); if msg.done { self.nbars.fetch_sub(1, Ordering::SeqCst); continue; } out.clear(); let mut state = self.state.lock().unwrap(); state.lines[msg.level] = msg.string; // and draw if !first { out += &move_cursor_up(state.nlines); } else { first = false; } for l in state.lines.iter() { out.push_str(&format!("\r{}\n", l)); } printfl!(state.handle, "{}", out); } } } pub struct Pipe { level: usize, chan: Sender, } impl Write for Pipe { fn write(&mut self, buf: &[u8]) -> Result { let s = from_utf8(buf).unwrap().to_owned(); self.chan .send(WriteMsg { // finish method emit empty string done: s.is_empty(), level: self.level, string: s, }) .unwrap(); Ok(1) } fn flush(&mut self) -> Result<()> { Ok(()) } } // WriteMsg is the message format used to communicate // between MultiBar and its bars struct WriteMsg { done: bool, level: usize, string: String, } pbr-1.1.1/src/pb.rs000064400000000000000000000433701046102023000121540ustar 00000000000000use crate::tty::{terminal_size, Width}; use std::io::Stdout; use std::io::{self, Write}; use std::time::{Duration, Instant}; macro_rules! kb_fmt { ($n: ident) => {{ let kb = 1024f64; match $n { $n if $n >= kb.powf(4_f64) => format!("{:.*} TB", 2, $n / kb.powf(4_f64)), $n if $n >= kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)), $n if $n >= kb.powf(2_f64) => format!("{:.*} MB", 2, $n / kb.powf(2_f64)), $n if $n >= kb => format!("{:.*} KB", 2, $n / kb), _ => format!("{:.*} B", 0, $n), } }}; } const FORMAT: &str = "[=>-]"; const TICK_FORMAT: &str = "\\|/-"; // Output type format, indicate which format wil be used in // the speed box. #[derive(Debug)] pub enum Units { Default, Bytes, } pub struct ProgressBar { start_time: Instant, units: Units, pub total: u64, current: u64, bar_start: String, bar_current: String, bar_current_n: String, bar_remain: String, bar_end: String, tick: Vec, tick_state: usize, width: Option, message: String, last_refresh_time: Instant, max_refresh_rate: Option, pub is_finish: bool, pub is_multibar: bool, pub show_bar: bool, pub show_speed: bool, pub show_percent: bool, pub show_counter: bool, pub show_time_left: bool, pub show_tick: bool, pub show_message: bool, handle: T, } impl ProgressBar { /// Create a new ProgressBar with default configuration. /// /// # Examples /// /// ```no_run /// use std::thread; /// use pbr::{ProgressBar, Units}; /// /// let count = 1000; /// let mut pb = ProgressBar::new(count); /// pb.set_units(Units::Bytes); /// /// for _ in 0..count { /// pb.inc(); /// thread::sleep_ms(100); /// } /// ``` pub fn new(total: u64) -> ProgressBar { let handle = ::std::io::stdout(); ProgressBar::on(handle, total) } } impl ProgressBar { /// Create a new ProgressBar with default configuration but /// pass an arbitrary writer. /// /// # Examples /// /// ```no_run /// use std::thread; /// use std::io::stderr; /// use pbr::{ProgressBar, Units}; /// /// let count = 1000; /// let mut pb = ProgressBar::on(stderr(), count); /// pb.set_units(Units::Bytes); /// /// for _ in 0..count { /// pb.inc(); /// thread::sleep_ms(100); /// } /// ``` pub fn on(handle: T, total: u64) -> ProgressBar { let mut pb = ProgressBar { total, current: 0, start_time: Instant::now(), units: Units::Default, is_finish: false, is_multibar: false, show_bar: true, show_speed: true, show_percent: true, show_counter: true, show_time_left: true, show_tick: false, show_message: true, bar_start: String::new(), bar_current: String::new(), bar_current_n: String::new(), bar_remain: String::new(), bar_end: String::new(), tick: Vec::new(), tick_state: 0, width: None, message: String::new(), last_refresh_time: Instant::now(), max_refresh_rate: None, handle, }; pb.format(FORMAT); pb.tick_format(TICK_FORMAT); pb } /// Set units, default is simple numbers /// /// # Examples /// /// ```no_run /// use pbr::{ProgressBar, Units}; /// /// let n_bytes = 100; /// let mut pb = ProgressBar::new(n_bytes); /// pb.set_units(Units::Bytes); /// ``` pub fn set_units(&mut self, u: Units) { self.units = u; } /// Set custom format to the drawing bar, default is `[=>-]` /// /// # Examples /// /// ```ignore /// let mut pb = ProgressBar::new(...); /// pb.format("[=>_]"); /// ``` pub fn format(&mut self, fmt: &str) { if fmt.len() >= 5 { let v: Vec<&str> = fmt.split("").collect(); self.bar_start = v[1].to_owned(); self.bar_current = v[2].to_owned(); self.bar_current_n = v[3].to_owned(); self.bar_remain = v[4].to_owned(); self.bar_end = v[5].to_owned(); } } /// Set message to display in the prefix, call with "" to stop printing a message. /// /// All newlines are replaced with spaces. /// /// # Examples /// ```ignore /// let mut pb = ProgressBar::new(20); /// /// for x in 0..20 { /// match x { /// 0 => pb.message("Doing 1st Quarter"), /// 5 => pb.message("Doing 2nd Quarter"), /// 10 => pb.message("Doing 3rd Quarter"), /// 15 => pb.message("Doing 4th Quarter"), /// } /// pb.inc(). /// } /// /// ``` pub fn message(&mut self, message: &str) { self.message = message.replace(['\n', '\r'], " ") } /// Set tick format for the progressBar, default is \\|/- /// /// Format is not limited to 4 characters, any string can /// be used as a tick format (the tick will successively /// take the value of each char but won't loop backwards). /// /// /// # Examples /// ```ignore /// let mut pb = ProgressBar::new(...); /// pb.tick_format("▀▐▄▌") /// ``` pub fn tick_format(&mut self, tick_fmt: &str) { if tick_fmt != TICK_FORMAT { self.show_tick = true; } self.tick = tick_fmt .split("") .map(|x| x.to_owned()) .filter(|x| !x.is_empty()) .collect(); } /// Set width, or `None` for default. /// /// # Examples /// /// ```ignore /// let mut pb = ProgressBar::new(...); /// pb.set_width(Some(80)); /// ``` pub fn set_width(&mut self, w: Option) { self.width = w; } /// Set max refresh rate, above which the progress bar will not redraw, or `None` for none. /// /// # Examples /// /// ```ignore /// let mut pb = ProgressBar::new(...); /// pb.set_max_refresh_rate(Some(Duration::from_millis(100))); /// ``` pub fn set_max_refresh_rate(&mut self, w: Option) { self.max_refresh_rate = w; if let Some(dur) = self.max_refresh_rate { self.last_refresh_time = self.last_refresh_time - dur; } } /// Update progress bar even though no progress are made /// Useful to see if a program is bricked or just /// not doing any progress. /// /// tick is not needed with add or inc /// as performed operation take place /// in draw function. /// /// # Examples /// ```ignore /// let mut pb = ProgressBar::new(...); /// pb.inc(); /// for _ in ... { /// ...do something /// pb.tick(); /// } /// pb.finish(); /// ``` pub fn tick(&mut self) { self.tick_state = (self.tick_state + 1) % self.tick.len(); if self.current <= self.total { self.draw() } } /// Add to current value /// /// # Examples /// /// ```no_run /// use pbr::ProgressBar; /// /// let mut pb = ProgressBar::new(10); /// pb.add(5); /// pb.finish(); /// ``` pub fn add(&mut self, i: u64) -> u64 { self.current += i; self.tick(); self.current } /// Manually set the current value of the bar /// /// # Examples /// ```no_run /// use pbr::ProgressBar; /// /// let mut pb = ProgressBar::new(10); /// pb.set(8); /// pb.finish(); pub fn set(&mut self, i: u64) -> u64 { self.current = i; self.tick(); self.current } /// Increment current value pub fn inc(&mut self) -> u64 { self.add(1) } /// Resets the start time to now pub fn reset_start_time(&mut self) { self.start_time = Instant::now(); } fn draw(&mut self) { let now = Instant::now(); if let Some(mrr) = self.max_refresh_rate { if now - self.last_refresh_time < mrr && self.current < self.total { return; } } let mut time_elapsed = now - self.start_time; if time_elapsed.is_zero() { time_elapsed = Duration::from_nanos(1); } let speed = self.current as f64 / time_elapsed.as_secs_f64(); let width = self.width(); let mut out; let mut parts = Vec::new(); let mut base = String::new(); let mut prefix = String::new(); let mut suffix = String::from(" "); // precent box if self.show_percent { let percent = self.current as f64 / (self.total as f64 / 100f64); parts.push(format!( "{:.*} %", 2, if percent.is_nan() { 0.0 } else { percent } )); } // speed box if self.show_speed { match self.units { Units::Default => parts.push(format!("{:.*}/s", 2, speed)), Units::Bytes => parts.push(format!("{}/s", kb_fmt!(speed))), }; } // time left box if self.show_time_left && self.current > 0 && self.total > self.current { let left = 1. / speed * (self.total - self.current) as f64; if left < 60. { parts.push(format!("{:.0}s", left)); } else { parts.push(format!("{:.0}m", left / 60.)); }; } suffix += &parts.join(" "); // message box if self.show_message { prefix = prefix + &self.message; } // counter box if self.show_counter { let (c, t) = (self.current as f64, self.total as f64); prefix = prefix + &match self.units { Units::Default => format!("{} / {} ", c, t), Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)), }; } // tick box if self.show_tick { prefix = prefix + &format!("{} ", self.tick[self.tick_state]); } // bar box if self.show_bar { let p = prefix.chars().count() + suffix.chars().count() + 3; if p < width { let size = width - p; let curr_count = ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize; if size >= curr_count { let rema_count = size - curr_count; base = self.bar_start.clone(); if rema_count > 0 && curr_count > 0 { base = base + &self.bar_current.repeat(curr_count - 1) + &self.bar_current_n; } else { base = base + &self.bar_current.repeat(curr_count); } base = base + &self.bar_remain.repeat(rema_count) + &self.bar_end; } } } out = prefix + &base + &suffix; // pad if out.len() < width { let gap = width - out.len(); out = out + &" ".repeat(gap); } // print printfl!(self.handle, "\r{}", out); self.last_refresh_time = Instant::now(); } // finish_draw ensure that the progress bar is reached to its end, and do the // last drawing if needed. fn finish_draw(&mut self) { let mut redraw = false; if let Some(mrr) = self.max_refresh_rate { if Instant::now() - self.last_refresh_time < mrr { self.max_refresh_rate = None; redraw = true; } } if self.current < self.total { self.current = self.total; redraw = true; } if redraw { self.draw(); } self.is_finish = true; } /// Calling finish manually will set current to total and draw /// the last time pub fn finish(&mut self) { self.finish_draw(); printfl!(self.handle, ""); } /// Call finish and write string `s` that will replace the progress bar. pub fn finish_print(&mut self, s: &str) { self.finish_draw(); let width = self.width(); let mut out = s.to_owned(); if s.len() < width { out += &" ".repeat(width - s.len()); }; printfl!(self.handle, "\r{}", out); self.finish(); } /// Call finish and write string `s` below the progress bar. /// /// If the ProgressBar is part of MultiBar instance, you should use /// `finish_print` to print message. pub fn finish_println(&mut self, s: &str) { // `finish_println` does not allow in MultiBar mode, because printing // new line will break the multiBar output. if self.is_multibar { return self.finish_print(s); } self.finish_draw(); printfl!(self.handle, "\n{}", s); } /// Get terminal width, from configuration, terminal size, or default(80) fn width(&mut self) -> usize { if let Some(w) = self.width { w } else if let Some((Width(w), _)) = terminal_size() { w as usize } else { 80 } } } // Implement io::Writer impl Write for ProgressBar { fn write(&mut self, buf: &[u8]) -> io::Result { let n = buf.len(); self.add(n as u64); Ok(n) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } #[cfg(test)] mod test { use crate::{ProgressBar, Units}; use std::time::Duration; #[test] fn add() { let mut pb = ProgressBar::new(10); pb.add(2); assert!(pb.current == 2, "should add the given `n` to current"); assert!( pb.add(2) == pb.current, "add should return the current value" ); } #[test] fn inc() { let mut pb = ProgressBar::new(10); pb.inc(); assert!(pb.current == 1, "should increment current by 1"); } #[test] fn format() { let fmt = "[~> ]"; let mut pb = ProgressBar::new(1); pb.format(fmt); assert!( pb.bar_start + &pb.bar_current + &pb.bar_current_n + &pb.bar_remain + &pb.bar_end == fmt ); } #[test] fn finish() { let mut pb = ProgressBar::new(10); pb.finish(); assert!(pb.current == pb.total, "should set current to total"); assert!(pb.is_finish, "should set is_finish to true"); } #[test] fn kb_fmt() { let kb = 1024f64; let mb = kb.powf(2f64); let gb = kb.powf(3f64); let tb = kb.powf(4f64); assert_eq!(kb_fmt!(kb), "1.00 KB"); assert_eq!(kb_fmt!(mb), "1.00 MB"); assert_eq!(kb_fmt!(gb), "1.00 GB"); assert_eq!(kb_fmt!(tb), "1.00 TB"); } #[test] fn disable_speed_percent() { let mut out = Vec::new(); let mut pb = ProgressBar::on(&mut out, 10); pb.show_speed = false; pb.show_percent = false; pb.set_width(Some(80)); pb.add(2); assert_eq!( std::str::from_utf8(&out).unwrap(), "\r2 / 10 [=============>-----------------------------------------------------] 0s ", ); } #[test] fn disable_speed_time_left() { let mut out = Vec::new(); let mut pb = ProgressBar::on(&mut out, 10); pb.show_speed = false; pb.show_time_left = false; pb.set_width(Some(65)); pb.add(1); assert_eq!( std::str::from_utf8(&out).unwrap(), "\r1 / 10 [====>------------------------------------------] 10.00 % ", ); } #[test] fn disable_percent_time_left() { let mut out = Vec::new(); let mut pb = ProgressBar::on(&mut out, 10); pb.show_percent = false; pb.show_time_left = false; pb.set_units(Units::Bytes); pb.set_width(Some(65)); pb.draw(); assert_eq!( std::str::from_utf8(&out).unwrap(), "\r0 B / 10 B [---------------------------------------------] 0 B/s ", ); } #[test] fn disable_suffix() { let mut out = Vec::new(); let mut pb = ProgressBar::on(&mut out, 10); pb.show_speed = false; pb.show_percent = false; pb.show_time_left = false; pb.set_units(Units::Bytes); pb.set_width(Some(65)); pb.draw(); assert_eq!( std::str::from_utf8(&out).unwrap(), "\r0 B / 10 B [--------------------------------------------------] ", ); } #[test] fn max_refresh_rate_finish() { let count = 500; let mut out = Vec::new(); let mut pb = ProgressBar::on(&mut out, count); pb.format("╢▌▌░╟"); pb.set_width(Some(80)); pb.set_max_refresh_rate(Some(Duration::from_millis(100))); pb.show_speed = false; pb.show_time_left = false; pb.add(count / 2); pb.add(count / 2); let mut split = std::str::from_utf8(&out) .unwrap() .trim_start_matches('\r') .split('\r'); assert_eq!( split.next(), Some("250 / 500 ╢▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌░░░░░░░░░░░░░░░░░░░░░░░░░░░░░╟ 50.00 %") ); assert_eq!( split.next(), Some("500 / 500 ╢▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌╟ 100.00 %") ); } } pbr-1.1.1/src/tty/mod.rs000064400000000000000000000011351046102023000131430ustar 00000000000000//! Most of the code in for the `terminal_size()` function taken from: //! https://github.com/eminence/terminal-size //! //! A simple utility for getting the size of a terminal, and moving `n` lines up. //! //! Supports both Linux and Windows, but help is needed to test other platforms //! //! #[derive(Debug)] pub struct Width(pub u16); #[derive(Debug)] pub struct Height(pub u16); #[cfg(unix)] mod unix; #[cfg(unix)] pub use self::unix::*; #[cfg(target_os = "wasi")] mod wasi; #[cfg(target_os = "wasi")] pub use self::wasi::*; #[cfg(windows)] mod windows; #[cfg(windows)] pub use self::windows::*; pbr-1.1.1/src/tty/unix.rs000064400000000000000000000045271046102023000133570ustar 00000000000000use super::{Height, Width}; // We need to convert from c_int to c_ulong at least on DragonFly and FreeBSD. #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] fn ioctl_conv>(v: T) -> libc::c_ulong { v.into() } // No-op on any other operating system. #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))] fn ioctl_conv(v: T) -> T { v } /// Returns the size of the terminal, if available. /// /// If STDOUT is not a tty, returns `None` pub fn terminal_size() -> Option<(Width, Height)> { use libc::{ioctl, isatty, winsize, STDOUT_FILENO, TIOCGWINSZ}; let is_tty: bool = unsafe { isatty(STDOUT_FILENO) == 1 }; if !is_tty { return None; } let (rows, cols) = unsafe { let mut winsize = winsize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0, }; ioctl(STDOUT_FILENO, ioctl_conv(TIOCGWINSZ), &mut winsize); let rows = if winsize.ws_row > 0 { winsize.ws_row } else { 0 }; let cols = if winsize.ws_col > 0 { winsize.ws_col } else { 0 }; (rows as u16, cols as u16) }; if rows > 0 && cols > 0 { Some((Width(cols), Height(rows))) } else { None } } /// Return string that move the cursor `n` lines up. pub fn move_cursor_up(n: usize) -> String { format!("\x1B[{}A", n) } #[cfg(not(target_os = "redox"))] #[test] /// Compare with the output of `stty size` fn compare_with_stty() { use std::process::Command; use std::process::Stdio; let mut args = vec!["-F", "/dev/stderr", "size"]; if cfg!(target_os = "macos") { args[0] = "-f" } let output = Command::new("stty") .args(&args) .stderr(Stdio::inherit()) .output() .unwrap(); let stdout = String::from_utf8(output.stdout).unwrap(); assert!(output.status.success()); // stdout is "rows cols" let mut data = stdout.split_whitespace(); let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap(); let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap(); println!("{}", stdout); println!("{} {}", rows, cols); if let Some((Width(w), Height(h))) = terminal_size() { assert_eq!(rows, h); assert_eq!(cols, w); } } pbr-1.1.1/src/tty/wasi.rs000064400000000000000000000006571046102023000133370ustar 00000000000000use super::{Height, Width}; /// For WASI so far it will return none /// /// For background https://github.com/WebAssembly/WASI/issues/42 pub fn terminal_size() -> Option<(Width, Height)> { return None; } /// This is inherited from unix and will work only when wasi executed on unix. /// /// For background https://github.com/WebAssembly/WASI/issues/42 pub fn move_cursor_up(n: usize) -> String { format!("\x1B[{}A", n) } pbr-1.1.1/src/tty/windows.rs000064400000000000000000000036311046102023000140610ustar 00000000000000use super::{Height, Width}; /// Returns the size of the terminal, if available. /// /// Note that this returns the size of the actual command window, and /// not the overall size of the command window buffer pub fn terminal_size() -> Option<(Width, Height)> { if let Some((_, csbi)) = get_csbi() { let w: Width = Width((csbi.srWindow.Right - csbi.srWindow.Left) as u16); let h: Height = Height((csbi.srWindow.Bottom - csbi.srWindow.Top) as u16); Some((w, h)) } else { None } } /// move the cursor `n` lines up; return an empty string, just to /// be aligned with the unix version. pub fn move_cursor_up(n: usize) -> String { use winapi::um::wincon::{SetConsoleCursorPosition, COORD}; if let Some((hand, csbi)) = get_csbi() { unsafe { SetConsoleCursorPosition( hand, COORD { X: 0, Y: csbi.dwCursorPosition.Y - n as i16, }, ); } } "".to_string() } fn get_csbi() -> Option<( winapi::shared::ntdef::HANDLE, winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO, )> { use winapi::shared::ntdef::HANDLE; use winapi::um::processenv::GetStdHandle; use winapi::um::winbase::STD_OUTPUT_HANDLE; use winapi::um::wincon::{ GetConsoleScreenBufferInfo, CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT, }; let hand: HANDLE = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) }; let zc = COORD { X: 0, Y: 0 }; let mut csbi = CONSOLE_SCREEN_BUFFER_INFO { dwSize: zc.clone(), dwCursorPosition: zc.clone(), wAttributes: 0, srWindow: SMALL_RECT { Left: 0, Top: 0, Right: 0, Bottom: 0, }, dwMaximumWindowSize: zc, }; match unsafe { GetConsoleScreenBufferInfo(hand, &mut csbi) } { 0 => None, _ => Some((hand, csbi)), } } pbr-1.1.1/tests/lib.rs000064400000000000000000000055441046102023000126750ustar 00000000000000extern crate pbr; use pbr::{PbIter, ProgressBar}; use std::thread; use std::time::Duration; #[test] fn simple_example() { let count = 5000; let mut pb = ProgressBar::new(count); pb.format("╢▌▌░╟"); for _ in 0..count { pb.inc(); thread::sleep(Duration::from_millis(5)); } pb.finish_println("done!"); } #[test] fn custom_width_example() { let count = 500; let mut pb = ProgressBar::new(count); pb.set_width(Some(80)); pb.format("╢▌▌░╟"); for _ in 0..count { pb.inc(); thread::sleep(Duration::from_millis(5)); } pb.finish_println("done!"); } #[test] fn simple_iter_example() { for _ in PbIter::new(0..2000) { thread::sleep(Duration::from_millis(1)); } } #[test] fn timeout_example() { let count = 10; let mut pb = ProgressBar::new(count * 20); pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏"); pb.show_message = true; pb.inc(); for _ in 0..count { for _ in 0..20 { pb.message("Waiting : "); thread::sleep(Duration::from_millis(50)); pb.tick(); } for _ in 0..20 { pb.message("Connected: "); thread::sleep(Duration::from_millis(50)); pb.inc(); } } for _ in 0..10 { pb.message("Cleaning :"); thread::sleep(Duration::from_millis(50)); pb.tick(); } pb.finish_println("done!"); } #[test] // see: issue #11 fn tick_before_start() { let count = 100; let mut pb = ProgressBar::new(count); pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏"); pb.tick(); for _ in 0..count { pb.tick(); thread::sleep(Duration::from_millis(50)); } for _ in 0..count { pb.inc(); thread::sleep(Duration::from_millis(50)); } } #[test] fn npm_bar() { let count = 30; let mut pb = ProgressBar::new(count * 5); pb.tick_format("\\|/-"); pb.format("|#--|"); pb.show_tick = true; pb.show_speed = false; pb.show_percent = false; pb.show_counter = false; pb.show_time_left = false; pb.inc(); for _ in 0..count { for _ in 0..5 { pb.message("normalize -> thing "); thread::sleep(Duration::from_millis(80)); pb.tick(); } for _ in 0..5 { pb.message("fuzz -> tree "); thread::sleep(Duration::from_millis(80)); pb.inc(); } } pb.finish_println("done!"); } #[test] // see: issue 45# fn final_redraw_max_refresh_rate() { let count = 500; let mut pb = ProgressBar::new(count); pb.format("╢▌▌░╟"); pb.set_max_refresh_rate(Some(Duration::from_millis(100))); for _ in 0..count { pb.inc(); thread::sleep(Duration::from_millis(5)); } pb.finish_println("done!"); }