Skip to content

Commit

Permalink
Allow custom template keys in ProgressStyle
Browse files Browse the repository at this point in the history
Tweak custom-template example

Adjust visibility of `ProgressState`

Make `Estimate` struct have pub(crate) visibility again

Tweak visibility

Rename variable

Add doc comment back in
  • Loading branch information
redzic committed Jul 6, 2021
1 parent 1e6cf26 commit 5ab0521
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 38 deletions.
26 changes: 26 additions & 0 deletions examples/custom-template.rs
@@ -0,0 +1,26 @@
use std::thread;
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

fn main() {
const SIZE: u64 = 1 << 12;

let pb = ProgressBar::new(SIZE);

pb.set_style(
ProgressStyle::default_bar()
.template("{per_sec_precise} {wide_bar} {percent}% ({pos}/{len})")
.key("per_sec_precise", |s| format!("{:>5.1}/s", s.per_sec())),
);

pb.set_position(0);
thread::sleep(Duration::from_secs(1));

for i in 1..=SIZE {
pb.inc(1);
thread::sleep(Duration::from_secs_f64(2.0 / ((2 * i) as f64)));
}

pb.finish();
}
4 changes: 2 additions & 2 deletions src/progress_bar.rs
Expand Up @@ -187,7 +187,7 @@ impl ProgressBar {
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);
state.draw_next = state.pos.saturating_add(state.per_sec() as u64 / n);
}

/// Manually ticks the spinner or progress bar
Expand Down Expand Up @@ -478,7 +478,7 @@ impl ProgressBar {

/// Returns the current rate of progress
pub fn per_sec(&self) -> u64 {
self.state.lock().unwrap().per_sec()
self.state.lock().unwrap().per_sec() as u64
}

/// Returns the current expected duration
Expand Down
29 changes: 17 additions & 12 deletions src/state.rs
Expand Up @@ -8,10 +8,10 @@ use crate::draw_target::{ProgressDrawState, ProgressDrawTarget};
use crate::style::{ProgressFinish, ProgressStyle};

/// The state of a progress bar at a moment in time.
pub(crate) struct ProgressState {
pub struct ProgressState {
pub(crate) style: ProgressStyle,
pub(crate) pos: u64,
pub(crate) len: u64,
pub pos: u64,
pub len: u64,
pub(crate) tick: u64,
pub(crate) started: Instant,
pub(crate) draw_target: ProgressDrawTarget,
Expand Down Expand Up @@ -82,9 +82,14 @@ impl ProgressState {
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 position of the status bar.
pub fn position(&self) -> u64 {
self.pos
}

/// Returns the length of the status bar.
pub fn length(&self) -> u64 {
self.len
}

/// Returns the current message of the progress bar.
Expand Down Expand Up @@ -120,12 +125,12 @@ impl ProgressState {
}

/// The number of steps per second
pub fn per_sec(&self) -> u64 {
let avg_time = self.est.seconds_per_step();
if avg_time == 0.0 {
0
pub fn per_sec(&self) -> f64 {
let per_sec = 1.0 / self.est.seconds_per_step();
if per_sec.is_nan() {
0.0
} else {
(1.0 / avg_time) as u64
per_sec
}
}

Expand Down Expand Up @@ -157,7 +162,7 @@ impl ProgressState {
}
if new_pos >= self.draw_next {
self.draw_next = new_pos.saturating_add(if self.draw_rate != 0 {
self.per_sec() / self.draw_rate
self.per_sec() as u64 / self.draw_rate
} else {
self.draw_delta
});
Expand Down
99 changes: 75 additions & 24 deletions src/style.rs
Expand Up @@ -4,19 +4,81 @@ use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, Hu
use crate::state::ProgressState;
use crate::utils::{expand_template, pad_str};
use std::borrow::Cow;
use std::collections::HashMap;

#[cfg(feature = "improved_unicode")]
use unicode_segmentation::UnicodeSegmentation;

pub type Format = fn(&ProgressState) -> String;
type FormatMapInner = HashMap<&'static str, Format>;

#[derive(Clone)]
pub(crate) struct FormatMap(FormatMapInner);

impl Default for FormatMap {
fn default() -> Self {
let mut map: FormatMapInner = HashMap::with_capacity(32);

map.insert("spinner", |state| state.current_tick_str().to_string());
map.insert("msg", |state| state.message().to_string());
map.insert("prefix", |state| state.prefix().to_string());
map.insert("pos", |state| state.position().to_string());
map.insert("len", |state| state.length().to_string());
map.insert("percent", |state| {
format!("{:.*}", 0, state.fraction() * 100f32)
});
map.insert("bytes", |state| format!("{}", HumanBytes(state.pos)));
map.insert("total_bytes", |state| format!("{}", HumanBytes(state.len)));
map.insert("decimal_bytes", |state| {
format!("{}", DecimalBytes(state.pos))
});
map.insert("decimal_total_bytes", |state| {
format!("{}", DecimalBytes(state.len))
});
map.insert("binary_bytes", |state| {
format!("{}", BinaryBytes(state.pos))
});
map.insert("binary_total_bytes", |state| {
format!("{}", BinaryBytes(state.len))
});
map.insert("elapsed_precise", |state| {
format!("{}", FormattedDuration(state.started.elapsed()))
});
map.insert("elapsed", |state| {
format!("{:#}", HumanDuration(state.started.elapsed()))
});
map.insert("per_sec", |state| format!("{:.0}/s", state.per_sec()));
map.insert("bytes_per_sec", |state| {
format!("{}/s", HumanBytes(state.per_sec() as u64))
});
map.insert("binary_bytes_per_sec", |state| {
format!("{}/s", BinaryBytes(state.per_sec() as u64))
});
map.insert("eta_precise", |state| {
format!("{}", FormattedDuration(state.eta()))
});
map.insert("eta", |state| format!("{:#}", HumanDuration(state.eta())));
map.insert("duration_precise", |state| {
format!("{}", FormattedDuration(state.duration()))
});
map.insert("duration", |state| {
format!("{:#}", HumanDuration(state.duration()))
});

FormatMap(map)
}
}

/// Controls the rendering style of progress bars
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct ProgressStyle {
tick_strings: Vec<Box<str>>,
progress_chars: Vec<Box<str>>,
template: Box<str>,
on_finish: ProgressFinish,
// how unicode-big each char in progress_chars is
char_width: usize,
format_map: FormatMap,
}

#[cfg(feature = "improved_unicode")]
Expand Down Expand Up @@ -70,6 +132,7 @@ impl ProgressStyle {
char_width,
template: "{wide_bar} {pos}/{len}".into(),
on_finish: ProgressFinish::default(),
format_map: FormatMap::default(),
}
}

Expand All @@ -86,6 +149,7 @@ impl ProgressStyle {
char_width,
template: "{spinner} {msg}".into(),
on_finish: ProgressFinish::default(),
format_map: FormatMap::default(),
}
}

Expand Down Expand Up @@ -129,6 +193,12 @@ impl ProgressStyle {
self
}

/// Adds a custom key parser that references a `&ProgressState` to the template.
pub fn key(mut self, key: &'static str, f: Format) -> ProgressStyle {
self.format_map.0.insert(key, f);
self
}

/// Sets the template string for the progress bar
///
/// Review the [list of template keys](./index.html#templates) for more information.
Expand Down Expand Up @@ -226,7 +296,6 @@ impl ProgressStyle {
}

pub(crate) fn format_state(&self, state: &ProgressState) -> Vec<String> {
let (pos, len) = state.position();
let mut rv = vec![];

for line in self.template.lines() {
Expand All @@ -242,32 +311,14 @@ impl ProgressStyle {
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(),
key => match self.format_map.0.get(key) {
Some(func) => func(state),
None => "".into(),
},
});

rv.push(if let Some(ref var) = wide_element {
Expand Down

0 comments on commit 5ab0521

Please sign in to comment.