Skip to content

Commit

Permalink
Allow custom template keys in ProgressStyle
Browse files Browse the repository at this point in the history
Mention in docs that custom keys cannot overide default ones
  • Loading branch information
redzic committed Jul 9, 2021
1 parent 1e6cf26 commit a59eefd
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 23 deletions.
6 changes: 4 additions & 2 deletions src/progress_bar.rs
Expand Up @@ -187,7 +187,9 @@ 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() / n as f64) as u64);
}

/// Manually ticks the spinner or progress bar
Expand Down Expand Up @@ -478,7 +480,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
23 changes: 9 additions & 14 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,11 +82,6 @@ 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 current message of the progress bar.
pub fn message(&self) -> &str {
&self.message
Expand Down Expand Up @@ -120,12 +115,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 +152,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() / self.draw_rate as f64) as u64
} else {
self.draw_delta
});
Expand Down
47 changes: 40 additions & 7 deletions src/style.rs
Expand Up @@ -4,19 +4,32 @@ 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;

#[derive(Clone)]
pub(crate) struct FormatMap(HashMap<&'static str, Format>);

impl Default for FormatMap {
fn default() -> Self {
FormatMap(HashMap::new())
}
}

/// 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 +83,7 @@ impl ProgressStyle {
char_width,
template: "{wide_bar} {pos}/{len}".into(),
on_finish: ProgressFinish::default(),
format_map: FormatMap::default(),
}
}

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

Expand Down Expand Up @@ -129,6 +144,14 @@ impl ProgressStyle {
self
}

/// Adds a custom key that references a `&ProgressState` to the template
///
/// Note that custom keys cannot override built-in template keys.
pub fn with_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 +249,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 @@ -249,8 +271,8 @@ impl ProgressStyle {
}
"msg" => state.message().to_string(),
"prefix" => state.prefix().to_string(),
"pos" => pos.to_string(),
"len" => len.to_string(),
"pos" => state.pos.to_string(),
"len" => state.len.to_string(),
"percent" => format!("{:.*}", 0, state.fraction() * 100f32),
"bytes" => format!("{}", HumanBytes(state.pos)),
"total_bytes" => format!("{}", HumanBytes(state.len)),
Expand All @@ -261,13 +283,24 @@ impl ProgressStyle {
"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())),
"bytes_per_sec" => format!("{}/s", HumanBytes(state.per_sec() as u64)),
"binary_bytes_per_sec" => format!("{}/s", BinaryBytes(state.per_sec() as u64)),
"eta_precise" => format!("{}", FormattedDuration(state.eta())),
"eta" => format!("{:#}", HumanDuration(state.eta())),
"duration_precise" => format!("{}", FormattedDuration(state.duration())),
"duration" => format!("{:#}", HumanDuration(state.duration())),
_ => "".into(),
key => {
// Avoid hash table lookup if the format map is empty,
// which is the common case.
if self.format_map.0.is_empty() {
"".into()
} else {
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 a59eefd

Please sign in to comment.