1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
//! progrs: A small library for displaying progress in terminal programs
//!
//! There are a number of libraries out there that can be used for
//! progress display, but in the author's opinion these libraries do
//! it *almost* right - either they eat up too much screen real estate
//! (by not sticking to one line per thing that should use progress)
//! or they try to align stuff left and right.
//!
//! In the author's humble opinion, the best example of just the right
//! amount of information vs screen real-estate is in the Git progress
//! output (when cloning, pulling, etc). It uses one line per thing,
//! and may display both percentage complete (in cases where it's
//! known) and even throughput (for network transfer).
//!
//! This library mimics the Git way of showing progress.
//!
//! # Example
//!
//! ```rust,no_run
//! let (mut n, nobjects) = (0, 218676);
//! let mut p = progrs::start("Collecting objects", Some(nobjects));
//! while n < nobjects {
//!     n += collect_more_objects();
//!     p.display(n);
//! }
//! p.stop("done");
//! ```
//!
//! which will produce the output:
//!
//! ```text
//! Collecting objects: 100% (218676/218676), done.
//! ```
//!
//! # TODO
//! - Add throughput display to `Progress`
use std::convert::Into;
use std::io;
use std::io::Write;

const CR: &'static str = "   \r";

#[derive(Debug)]
pub struct Progress {
    title: String,
    total: Option<u64>,
    last_value: u64
}

/// Start a new progress "bar"
pub fn start<T: Into<String>, N: Into<Option<u64>>>(title: T, total: N) -> Progress
    where T: Into<String>,
          N: Into<Option<u64>>
{
    Progress {
        title: title.into(),
        total: total.into(),
        last_value: 0,
    }
}

impl Progress {
    pub fn display(&mut self, n: u64) {
        self.last_value = n;

        let stdout = io::stdout();
        let mut h = stdout.lock();
        let _ = match self.total {
            Some(total) => {
                let pct = n as f64 * 100.0 / total as f64;
                write!(h, "{}: {:3.0}% ({}/{}){}",
                       self.title, pct, n, total, CR)
            }
            None => {
                write!(h, "{}: {}{}", self.title, n, CR)
            }
        };
        let _ = h.flush();
    }

    pub fn stop<M: AsRef<str>>(self, msg: M) {
        let _ = match self.total {
            Some(total) =>
                write!(io::stdout(), "{}: 100% ({}/{}), {}.\n",
                       self.title, total, total, msg.as_ref()),
            None =>
                write!(io::stdout(), "{}: {}, {}.\n", self.title, self.last_value, msg.as_ref())
        };
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn it_works() {
        let mut p = super::start("Receiving objects", Some(2323));
        for i in 0..2323 {
            p.display(i);
            ::std::thread::sleep(::std::time::Duration::from_millis(1));
        }
        p.stop("done");
    }
}