Skip to content

Commit

Permalink
Allow custom template keys in ProgressStyle
Browse files Browse the repository at this point in the history
  • Loading branch information
redzic authored and djc committed Jul 13, 2021
1 parent 1e6cf26 commit 51cc44a
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 53 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 @@ -477,7 +479,7 @@ impl ProgressBar {
}

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

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
104 changes: 67 additions & 37 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,12 @@ impl ProgressStyle {
self
}

/// Adds a custom key that references a `&ProgressState` to the template
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,48 +247,57 @@ 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() {
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()
let s = expand_template(line, |var| {
if let Some(formatter) = self.format_map.0.get(var.key) {
formatter(state)
} else {
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" => 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)),
"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() 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(),
}
}
"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 {
Expand Down

0 comments on commit 51cc44a

Please sign in to comment.