aboutsummaryrefslogtreecommitdiff
path: root/vendor/indicatif/src/style.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-07-19 15:37:58 +0300
committerValentin Popov <valentin@popov.link>2024-07-19 15:37:58 +0300
commita990de90fe41456a23e58bd087d2f107d321f3a1 (patch)
tree15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/indicatif/src/style.rs
parent3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff)
downloadfparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz
fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip
Deleted vendor folder
Diffstat (limited to 'vendor/indicatif/src/style.rs')
-rw-r--r--vendor/indicatif/src/style.rs987
1 files changed, 0 insertions, 987 deletions
diff --git a/vendor/indicatif/src/style.rs b/vendor/indicatif/src/style.rs
deleted file mode 100644
index 01b220f..0000000
--- a/vendor/indicatif/src/style.rs
+++ /dev/null
@@ -1,987 +0,0 @@
-use std::collections::HashMap;
-use std::fmt::{self, Write};
-use std::mem;
-#[cfg(not(target_arch = "wasm32"))]
-use std::time::Instant;
-
-use console::{measure_text_width, Style};
-#[cfg(target_arch = "wasm32")]
-use instant::Instant;
-#[cfg(feature = "unicode-segmentation")]
-use unicode_segmentation::UnicodeSegmentation;
-
-use crate::format::{
- BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanCount, HumanDuration,
- HumanFloatCount,
-};
-use crate::state::{ProgressState, TabExpandedString, DEFAULT_TAB_WIDTH};
-
-#[derive(Clone)]
-pub struct ProgressStyle {
- tick_strings: Vec<Box<str>>,
- progress_chars: Vec<Box<str>>,
- template: Template,
- // how unicode-big each char in progress_chars is
- char_width: usize,
- tab_width: usize,
- pub(crate) format_map: HashMap<&'static str, Box<dyn ProgressTracker>>,
-}
-
-#[cfg(feature = "unicode-segmentation")]
-fn segment(s: &str) -> Vec<Box<str>> {
- UnicodeSegmentation::graphemes(s, true)
- .map(|s| s.into())
- .collect()
-}
-
-#[cfg(not(feature = "unicode-segmentation"))]
-fn segment(s: &str) -> Vec<Box<str>> {
- s.chars().map(|x| x.to_string().into()).collect()
-}
-
-#[cfg(feature = "unicode-width")]
-fn measure(s: &str) -> usize {
- unicode_width::UnicodeWidthStr::width(s)
-}
-
-#[cfg(not(feature = "unicode-width"))]
-fn measure(s: &str) -> usize {
- s.chars().count()
-}
-
-/// finds the unicode-aware width of the passed grapheme cluters
-/// panics on an empty parameter, or if the characters are not equal-width
-fn width(c: &[Box<str>]) -> usize {
- c.iter()
- .map(|s| measure(s.as_ref()))
- .fold(None, |acc, new| {
- match acc {
- None => return Some(new),
- Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
- }
- acc
- })
- .unwrap()
-}
-
-impl ProgressStyle {
- /// Returns the default progress bar style for bars
- pub fn default_bar() -> Self {
- Self::new(Template::from_str("{wide_bar} {pos}/{len}").unwrap())
- }
-
- /// Returns the default progress bar style for spinners
- pub fn default_spinner() -> Self {
- Self::new(Template::from_str("{spinner} {msg}").unwrap())
- }
-
- /// Sets the template string for the progress bar
- ///
- /// Review the [list of template keys](../index.html#templates) for more information.
- pub fn with_template(template: &str) -> Result<Self, TemplateError> {
- Ok(Self::new(Template::from_str(template)?))
- }
-
- pub(crate) fn set_tab_width(&mut self, new_tab_width: usize) {
- self.tab_width = new_tab_width;
- self.template.set_tab_width(new_tab_width);
- }
-
- fn new(template: Template) -> Self {
- let progress_chars = segment("█░");
- let char_width = width(&progress_chars);
- Self {
- tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
- .chars()
- .map(|c| c.to_string().into())
- .collect(),
- progress_chars,
- char_width,
- template,
- format_map: HashMap::default(),
- tab_width: DEFAULT_TAB_WIDTH,
- }
- }
-
- /// Sets the tick character sequence for spinners
- ///
- /// Note that the last character is used as the [final tick string][Self::get_final_tick_str()].
- /// At least two characters are required to provide a non-final and final state.
- pub fn tick_chars(mut self, s: &str) -> Self {
- self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
- // Format bar will panic with some potentially confusing message, better to panic here
- // with a message explicitly informing of the problem
- assert!(
- self.tick_strings.len() >= 2,
- "at least 2 tick chars required"
- );
- self
- }
-
- /// Sets the tick string sequence for spinners
- ///
- /// Note that the last string is used as the [final tick string][Self::get_final_tick_str()].
- /// At least two strings are required to provide a non-final and final state.
- pub fn tick_strings(mut self, s: &[&str]) -> Self {
- self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
- // Format bar will panic with some potentially confusing message, better to panic here
- // with a message explicitly informing of the problem
- assert!(
- self.progress_chars.len() >= 2,
- "at least 2 tick strings required"
- );
- self
- }
-
- /// Sets the progress characters `(filled, current, to do)`
- ///
- /// You can pass more than three for a more detailed display.
- /// All passed grapheme clusters need to be of equal width.
- pub fn progress_chars(mut self, s: &str) -> Self {
- self.progress_chars = segment(s);
- // Format bar will panic with some potentially confusing message, better to panic here
- // with a message explicitly informing of the problem
- assert!(
- self.progress_chars.len() >= 2,
- "at least 2 progress chars required"
- );
- self.char_width = width(&self.progress_chars);
- self
- }
-
- /// Adds a custom key that owns a [`ProgressTracker`] to the template
- pub fn with_key<S: ProgressTracker + 'static>(mut self, key: &'static str, f: S) -> Self {
- self.format_map.insert(key, Box::new(f));
- self
- }
-
- /// Sets the template string for the progress bar
- ///
- /// Review the [list of template keys](../index.html#templates) for more information.
- pub fn template(mut self, s: &str) -> Result<Self, TemplateError> {
- self.template = Template::from_str(s)?;
- Ok(self)
- }
-
- fn current_tick_str(&self, state: &ProgressState) -> &str {
- match state.is_finished() {
- true => self.get_final_tick_str(),
- false => self.get_tick_str(state.tick),
- }
- }
-
- /// Returns the tick string for a given number
- pub fn get_tick_str(&self, idx: u64) -> &str {
- &self.tick_strings[(idx as usize) % (self.tick_strings.len() - 1)]
- }
-
- /// Returns the tick string for the finished state
- pub fn get_final_tick_str(&self) -> &str {
- &self.tick_strings[self.tick_strings.len() - 1]
- }
-
- fn format_bar(&self, fract: f32, width: usize, alt_style: Option<&Style>) -> BarDisplay<'_> {
- // The number of clusters from progress_chars to write (rounding down).
- let width = width / self.char_width;
- // The number of full clusters (including a fractional component for a partially-full one).
- let fill = fract * width as f32;
- // The number of entirely full clusters (by truncating `fill`).
- let entirely_filled = fill as usize;
- // 1 if the bar is not entirely empty or full (meaning we need to draw the "current"
- // character between the filled and "to do" segment), 0 otherwise.
- let head = usize::from(fill > 0.0 && entirely_filled < width);
-
- let cur = if head == 1 {
- // Number of fine-grained progress entries in progress_chars.
- let n = self.progress_chars.len().saturating_sub(2);
- let cur_char = if n <= 1 {
- // No fine-grained entries. 1 is the single "current" entry if we have one, the "to
- // do" entry if not.
- 1
- } else {
- // Pick a fine-grained entry, ranging from the last one (n) if the fractional part
- // of fill is 0 to the first one (1) if the fractional part of fill is almost 1.
- n.saturating_sub((fill.fract() * n as f32) as usize)
- };
- Some(cur_char)
- } else {
- None
- };
-
- // Number of entirely empty clusters needed to fill the bar up to `width`.
- let bg = width.saturating_sub(entirely_filled).saturating_sub(head);
- let rest = RepeatedStringDisplay {
- str: &self.progress_chars[self.progress_chars.len() - 1],
- num: bg,
- };
-
- BarDisplay {
- chars: &self.progress_chars,
- filled: entirely_filled,
- cur,
- rest: alt_style.unwrap_or(&Style::new()).apply_to(rest),
- }
- }
-
- pub(crate) fn format_state(
- &self,
- state: &ProgressState,
- lines: &mut Vec<String>,
- target_width: u16,
- ) {
- let mut cur = String::new();
- let mut buf = String::new();
- let mut wide = None;
-
- let pos = state.pos();
- let len = state.len().unwrap_or(pos);
- for part in &self.template.parts {
- match part {
- TemplatePart::Placeholder {
- key,
- align,
- width,
- truncate,
- style,
- alt_style,
- } => {
- buf.clear();
- if let Some(tracker) = self.format_map.get(key.as_str()) {
- tracker.write(state, &mut TabRewriter(&mut buf, self.tab_width));
- } else {
- match key.as_str() {
- "wide_bar" => {
- wide = Some(WideElement::Bar { alt_style });
- buf.push('\x00');
- }
- "bar" => buf
- .write_fmt(format_args!(
- "{}",
- self.format_bar(
- state.fraction(),
- width.unwrap_or(20) as usize,
- alt_style.as_ref(),
- )
- ))
- .unwrap(),
- "spinner" => buf.push_str(self.current_tick_str(state)),
- "wide_msg" => {
- wide = Some(WideElement::Message { align });
- buf.push('\x00');
- }
- "msg" => buf.push_str(state.message.expanded()),
- "prefix" => buf.push_str(state.prefix.expanded()),
- "pos" => buf.write_fmt(format_args!("{pos}")).unwrap(),
- "human_pos" => {
- buf.write_fmt(format_args!("{}", HumanCount(pos))).unwrap();
- }
- "len" => buf.write_fmt(format_args!("{len}")).unwrap(),
- "human_len" => {
- buf.write_fmt(format_args!("{}", HumanCount(len))).unwrap();
- }
- "percent" => buf
- .write_fmt(format_args!("{:.*}", 0, state.fraction() * 100f32))
- .unwrap(),
- "bytes" => buf.write_fmt(format_args!("{}", HumanBytes(pos))).unwrap(),
- "total_bytes" => {
- buf.write_fmt(format_args!("{}", HumanBytes(len))).unwrap();
- }
- "decimal_bytes" => buf
- .write_fmt(format_args!("{}", DecimalBytes(pos)))
- .unwrap(),
- "decimal_total_bytes" => buf
- .write_fmt(format_args!("{}", DecimalBytes(len)))
- .unwrap(),
- "binary_bytes" => {
- buf.write_fmt(format_args!("{}", BinaryBytes(pos))).unwrap();
- }
- "binary_total_bytes" => {
- buf.write_fmt(format_args!("{}", BinaryBytes(len))).unwrap();
- }
- "elapsed_precise" => buf
- .write_fmt(format_args!("{}", FormattedDuration(state.elapsed())))
- .unwrap(),
- "elapsed" => buf
- .write_fmt(format_args!("{:#}", HumanDuration(state.elapsed())))
- .unwrap(),
- "per_sec" => buf
- .write_fmt(format_args!("{}/s", HumanFloatCount(state.per_sec())))
- .unwrap(),
- "bytes_per_sec" => buf
- .write_fmt(format_args!("{}/s", HumanBytes(state.per_sec() as u64)))
- .unwrap(),
- "binary_bytes_per_sec" => buf
- .write_fmt(format_args!(
- "{}/s",
- BinaryBytes(state.per_sec() as u64)
- ))
- .unwrap(),
- "eta_precise" => buf
- .write_fmt(format_args!("{}", FormattedDuration(state.eta())))
- .unwrap(),
- "eta" => buf
- .write_fmt(format_args!("{:#}", HumanDuration(state.eta())))
- .unwrap(),
- "duration_precise" => buf
- .write_fmt(format_args!("{}", FormattedDuration(state.duration())))
- .unwrap(),
- "duration" => buf
- .write_fmt(format_args!("{:#}", HumanDuration(state.duration())))
- .unwrap(),
- _ => (),
- }
- };
-
- match width {
- Some(width) => {
- let padded = PaddedStringDisplay {
- str: &buf,
- width: *width as usize,
- align: *align,
- truncate: *truncate,
- };
- match style {
- Some(s) => cur
- .write_fmt(format_args!("{}", s.apply_to(padded)))
- .unwrap(),
- None => cur.write_fmt(format_args!("{padded}")).unwrap(),
- }
- }
- None => match style {
- Some(s) => cur.write_fmt(format_args!("{}", s.apply_to(&buf))).unwrap(),
- None => cur.push_str(&buf),
- },
- }
- }
- TemplatePart::Literal(s) => cur.push_str(s.expanded()),
- TemplatePart::NewLine => {
- self.push_line(lines, &mut cur, state, &mut buf, target_width, &wide);
- }
- }
- }
-
- if !cur.is_empty() {
- self.push_line(lines, &mut cur, state, &mut buf, target_width, &wide);
- }
- }
-
- fn push_line(
- &self,
- lines: &mut Vec<String>,
- cur: &mut String,
- state: &ProgressState,
- buf: &mut String,
- target_width: u16,
- wide: &Option<WideElement>,
- ) {
- let expanded = match wide {
- Some(inner) => inner.expand(mem::take(cur), self, state, buf, target_width),
- None => mem::take(cur),
- };
-
- // If there are newlines, we need to split them up
- // and add the lines separately so that they're counted
- // correctly on re-render.
- for (i, line) in expanded.split('\n').enumerate() {
- // No newlines found in this case
- if i == 0 && line.len() == expanded.len() {
- lines.push(expanded);
- break;
- }
-
- lines.push(line.to_string());
- }
- }
-}
-
-struct TabRewriter<'a>(&'a mut dyn fmt::Write, usize);
-
-impl Write for TabRewriter<'_> {
- fn write_str(&mut self, s: &str) -> fmt::Result {
- self.0
- .write_str(s.replace('\t', &" ".repeat(self.1)).as_str())
- }
-}
-
-#[derive(Clone, Copy)]
-enum WideElement<'a> {
- Bar { alt_style: &'a Option<Style> },
- Message { align: &'a Alignment },
-}
-
-impl<'a> WideElement<'a> {
- fn expand(
- self,
- cur: String,
- style: &ProgressStyle,
- state: &ProgressState,
- buf: &mut String,
- width: u16,
- ) -> String {
- let left = (width as usize).saturating_sub(measure_text_width(&cur.replace('\x00', "")));
- match self {
- Self::Bar { alt_style } => cur.replace(
- '\x00',
- &format!(
- "{}",
- style.format_bar(state.fraction(), left, alt_style.as_ref())
- ),
- ),
- WideElement::Message { align } => {
- buf.clear();
- buf.write_fmt(format_args!(
- "{}",
- PaddedStringDisplay {
- str: state.message.expanded(),
- width: left,
- align: *align,
- truncate: true,
- }
- ))
- .unwrap();
-
- let trimmed = match cur.as_bytes().last() == Some(&b'\x00') {
- true => buf.trim_end(),
- false => buf,
- };
-
- cur.replace('\x00', trimmed)
- }
- }
- }
-}
-
-#[derive(Clone, Debug)]
-struct Template {
- parts: Vec<TemplatePart>,
-}
-
-impl Template {
- fn from_str_with_tab_width(s: &str, tab_width: usize) -> Result<Self, TemplateError> {
- use State::*;
- let (mut state, mut parts, mut buf) = (Literal, vec![], String::new());
- for c in s.chars() {
- let new = match (state, c) {
- (Literal, '{') => (MaybeOpen, None),
- (Literal, '\n') => {
- if !buf.is_empty() {
- parts.push(TemplatePart::Literal(TabExpandedString::new(
- mem::take(&mut buf).into(),
- tab_width,
- )));
- }
- parts.push(TemplatePart::NewLine);
- (Literal, None)
- }
- (Literal, '}') => (DoubleClose, Some('}')),
- (Literal, c) => (Literal, Some(c)),
- (DoubleClose, '}') => (Literal, None),
- (MaybeOpen, '{') => (Literal, Some('{')),
- (MaybeOpen | Key, c) if c.is_ascii_whitespace() => {
- // If we find whitespace where the variable key is supposed to go,
- // backtrack and act as if this was a literal.
- buf.push(c);
- let mut new = String::from("{");
- new.push_str(&buf);
- buf.clear();
- parts.push(TemplatePart::Literal(TabExpandedString::new(
- new.into(),
- tab_width,
- )));
- (Literal, None)
- }
- (MaybeOpen, c) if c != '}' && c != ':' => (Key, Some(c)),
- (Key, c) if c != '}' && c != ':' => (Key, Some(c)),
- (Key, ':') => (Align, None),
- (Key, '}') => (Literal, None),
- (Key, '!') if !buf.is_empty() => {
- parts.push(TemplatePart::Placeholder {
- key: mem::take(&mut buf),
- align: Alignment::Left,
- width: None,
- truncate: true,
- style: None,
- alt_style: None,
- });
- (Width, None)
- }
- (Align, c) if c == '<' || c == '^' || c == '>' => {
- if let Some(TemplatePart::Placeholder { align, .. }) = parts.last_mut() {
- match c {
- '<' => *align = Alignment::Left,
- '^' => *align = Alignment::Center,
- '>' => *align = Alignment::Right,
- _ => (),
- }
- }
-
- (Width, None)
- }
- (Align, c @ '0'..='9') => (Width, Some(c)),
- (Align | Width, '!') => {
- if let Some(TemplatePart::Placeholder { truncate, .. }) = parts.last_mut() {
- *truncate = true;
- }
- (Width, None)
- }
- (Align, '.') => (FirstStyle, None),
- (Align, '}') => (Literal, None),
- (Width, c @ '0'..='9') => (Width, Some(c)),
- (Width, '.') => (FirstStyle, None),
- (Width, '}') => (Literal, None),
- (FirstStyle, '/') => (AltStyle, None),
- (FirstStyle, '}') => (Literal, None),
- (FirstStyle, c) => (FirstStyle, Some(c)),
- (AltStyle, '}') => (Literal, None),
- (AltStyle, c) => (AltStyle, Some(c)),
- (st, c) => return Err(TemplateError { next: c, state: st }),
- };
-
- match (state, new.0) {
- (MaybeOpen, Key) if !buf.is_empty() => parts.push(TemplatePart::Literal(
- TabExpandedString::new(mem::take(&mut buf).into(), tab_width),
- )),
- (Key, Align | Literal) if !buf.is_empty() => {
- parts.push(TemplatePart::Placeholder {
- key: mem::take(&mut buf),
- align: Alignment::Left,
- width: None,
- truncate: false,
- style: None,
- alt_style: None,
- });
- }
- (Width, FirstStyle | Literal) if !buf.is_empty() => {
- if let Some(TemplatePart::Placeholder { width, .. }) = parts.last_mut() {
- *width = Some(buf.parse().unwrap());
- buf.clear();
- }
- }
- (FirstStyle, AltStyle | Literal) if !buf.is_empty() => {
- if let Some(TemplatePart::Placeholder { style, .. }) = parts.last_mut() {
- *style = Some(Style::from_dotted_str(&buf));
- buf.clear();
- }
- }
- (AltStyle, Literal) if !buf.is_empty() => {
- if let Some(TemplatePart::Placeholder { alt_style, .. }) = parts.last_mut() {
- *alt_style = Some(Style::from_dotted_str(&buf));
- buf.clear();
- }
- }
- (_, _) => (),
- }
-
- state = new.0;
- if let Some(c) = new.1 {
- buf.push(c);
- }
- }
-
- if matches!(state, Literal | DoubleClose) && !buf.is_empty() {
- parts.push(TemplatePart::Literal(TabExpandedString::new(
- buf.into(),
- tab_width,
- )));
- }
-
- Ok(Self { parts })
- }
-
- fn from_str(s: &str) -> Result<Self, TemplateError> {
- Self::from_str_with_tab_width(s, DEFAULT_TAB_WIDTH)
- }
-
- fn set_tab_width(&mut self, new_tab_width: usize) {
- for part in &mut self.parts {
- if let TemplatePart::Literal(s) = part {
- s.set_tab_width(new_tab_width);
- }
- }
- }
-}
-
-#[derive(Debug)]
-pub struct TemplateError {
- state: State,
- next: char,
-}
-
-impl fmt::Display for TemplateError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "TemplateError: unexpected character {:?} in state {:?}",
- self.next, self.state
- )
- }
-}
-
-impl std::error::Error for TemplateError {}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-enum TemplatePart {
- Literal(TabExpandedString),
- Placeholder {
- key: String,
- align: Alignment,
- width: Option<u16>,
- truncate: bool,
- style: Option<Style>,
- alt_style: Option<Style>,
- },
- NewLine,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-enum State {
- Literal,
- MaybeOpen,
- DoubleClose,
- Key,
- Align,
- Width,
- FirstStyle,
- AltStyle,
-}
-
-struct BarDisplay<'a> {
- chars: &'a [Box<str>],
- filled: usize,
- cur: Option<usize>,
- rest: console::StyledObject<RepeatedStringDisplay<'a>>,
-}
-
-impl<'a> fmt::Display for BarDisplay<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- for _ in 0..self.filled {
- f.write_str(&self.chars[0])?;
- }
- if let Some(cur) = self.cur {
- f.write_str(&self.chars[cur])?;
- }
- self.rest.fmt(f)
- }
-}
-
-struct RepeatedStringDisplay<'a> {
- str: &'a str,
- num: usize,
-}
-
-impl<'a> fmt::Display for RepeatedStringDisplay<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- for _ in 0..self.num {
- f.write_str(self.str)?;
- }
- Ok(())
- }
-}
-
-struct PaddedStringDisplay<'a> {
- str: &'a str,
- width: usize,
- align: Alignment,
- truncate: bool,
-}
-
-impl<'a> fmt::Display for PaddedStringDisplay<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let cols = measure_text_width(self.str);
- let excess = cols.saturating_sub(self.width);
- if excess > 0 && !self.truncate {
- return f.write_str(self.str);
- } else if excess > 0 {
- let (start, end) = match self.align {
- Alignment::Left => (0, self.str.len() - excess),
- Alignment::Right => (excess, self.str.len()),
- Alignment::Center => (
- excess / 2,
- self.str.len() - excess.saturating_sub(excess / 2),
- ),
- };
-
- return f.write_str(self.str.get(start..end).unwrap_or(self.str));
- }
-
- let diff = self.width.saturating_sub(cols);
- let (left_pad, right_pad) = match self.align {
- Alignment::Left => (0, diff),
- Alignment::Right => (diff, 0),
- Alignment::Center => (diff / 2, diff.saturating_sub(diff / 2)),
- };
-
- for _ in 0..left_pad {
- f.write_char(' ')?;
- }
- f.write_str(self.str)?;
- for _ in 0..right_pad {
- f.write_char(' ')?;
- }
- Ok(())
- }
-}
-
-#[derive(PartialEq, Eq, Debug, Copy, Clone)]
-enum Alignment {
- Left,
- Center,
- Right,
-}
-
-/// Trait for defining stateful or stateless formatters
-pub trait ProgressTracker: Send + Sync {
- /// Creates a new instance of the progress tracker
- fn clone_box(&self) -> Box<dyn ProgressTracker>;
- /// Notifies the progress tracker of a tick event
- fn tick(&mut self, state: &ProgressState, now: Instant);
- /// Notifies the progress tracker of a reset event
- fn reset(&mut self, state: &ProgressState, now: Instant);
- /// Provides access to the progress bar display buffer for custom messages
- fn write(&self, state: &ProgressState, w: &mut dyn fmt::Write);
-}
-
-impl Clone for Box<dyn ProgressTracker> {
- fn clone(&self) -> Self {
- self.clone_box()
- }
-}
-
-impl<F> ProgressTracker for F
-where
- F: Fn(&ProgressState, &mut dyn fmt::Write) + Send + Sync + Clone + 'static,
-{
- fn clone_box(&self) -> Box<dyn ProgressTracker> {
- Box::new(self.clone())
- }
-
- fn tick(&mut self, _: &ProgressState, _: Instant) {}
-
- fn reset(&mut self, _: &ProgressState, _: Instant) {}
-
- fn write(&self, state: &ProgressState, w: &mut dyn fmt::Write) {
- (self)(state, w);
- }
-}
-
-#[cfg(test)]
-mod tests {
- use std::sync::Arc;
-
- use super::*;
- use crate::state::{AtomicPosition, ProgressState};
- use std::sync::Mutex;
-
- #[test]
- fn test_stateful_tracker() {
- #[derive(Debug, Clone)]
- struct TestTracker(Arc<Mutex<String>>);
-
- impl ProgressTracker for TestTracker {
- fn clone_box(&self) -> Box<dyn ProgressTracker> {
- Box::new(self.clone())
- }
-
- fn tick(&mut self, state: &ProgressState, _: Instant) {
- let mut m = self.0.lock().unwrap();
- m.clear();
- m.push_str(format!("{} {}", state.len().unwrap(), state.pos()).as_str());
- }
-
- fn reset(&mut self, _state: &ProgressState, _: Instant) {
- let mut m = self.0.lock().unwrap();
- m.clear();
- }
-
- fn write(&self, _state: &ProgressState, w: &mut dyn fmt::Write) {
- w.write_str(self.0.lock().unwrap().as_str()).unwrap();
- }
- }
-
- use crate::ProgressBar;
-
- let pb = ProgressBar::new(1);
- pb.set_style(
- ProgressStyle::with_template("{{ {foo} }}")
- .unwrap()
- .with_key("foo", TestTracker(Arc::new(Mutex::new(String::default()))))
- .progress_chars("#>-"),
- );
-
- let mut buf = Vec::new();
- let style = pb.clone().style();
-
- style.format_state(&pb.state().state, &mut buf, 16);
- assert_eq!(&buf[0], "{ }");
- buf.clear();
- pb.inc(1);
- style.format_state(&pb.state().state, &mut buf, 16);
- assert_eq!(&buf[0], "{ 1 1 }");
- pb.reset();
- buf.clear();
- style.format_state(&pb.state().state, &mut buf, 16);
- assert_eq!(&buf[0], "{ }");
- pb.finish_and_clear();
- }
-
- use crate::state::TabExpandedString;
-
- #[test]
- fn test_expand_template() {
- const WIDTH: u16 = 80;
- let pos = Arc::new(AtomicPosition::new());
- let state = ProgressState::new(Some(10), pos);
- let mut buf = Vec::new();
-
- let mut style = ProgressStyle::default_bar();
- style.format_map.insert(
- "foo",
- Box::new(|_: &ProgressState, w: &mut dyn Write| write!(w, "FOO").unwrap()),
- );
- style.format_map.insert(
- "bar",
- Box::new(|_: &ProgressState, w: &mut dyn Write| write!(w, "BAR").unwrap()),
- );
-
- style.template = Template::from_str("{{ {foo} {bar} }}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "{ FOO BAR }");
-
- buf.clear();
- style.template = Template::from_str(r#"{ "foo": "{foo}", "bar": {bar} }"#).unwrap();
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], r#"{ "foo": "FOO", "bar": BAR }"#);
- }
-
- #[test]
- fn test_expand_template_flags() {
- use console::set_colors_enabled;
- set_colors_enabled(true);
-
- const WIDTH: u16 = 80;
- let pos = Arc::new(AtomicPosition::new());
- let state = ProgressState::new(Some(10), pos);
- let mut buf = Vec::new();
-
- let mut style = ProgressStyle::default_bar();
- style.format_map.insert(
- "foo",
- Box::new(|_: &ProgressState, w: &mut dyn Write| write!(w, "XXX").unwrap()),
- );
-
- style.template = Template::from_str("{foo:5}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "XXX ");
-
- buf.clear();
- style.template = Template::from_str("{foo:.red.on_blue}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "\u{1b}[31m\u{1b}[44mXXX\u{1b}[0m");
-
- buf.clear();
- style.template = Template::from_str("{foo:^5.red.on_blue}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
-
- buf.clear();
- style.template = Template::from_str("{foo:^5.red.on_blue/green.on_cyan}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "\u{1b}[31m\u{1b}[44m XXX \u{1b}[0m");
- }
-
- #[test]
- fn align_truncation() {
- const WIDTH: u16 = 10;
- let pos = Arc::new(AtomicPosition::new());
- let mut state = ProgressState::new(Some(10), pos);
- let mut buf = Vec::new();
-
- let style = ProgressStyle::with_template("{wide_msg}").unwrap();
- state.message = TabExpandedString::NoTabs("abcdefghijklmnopqrst".into());
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "abcdefghij");
-
- buf.clear();
- let style = ProgressStyle::with_template("{wide_msg:>}").unwrap();
- state.message = TabExpandedString::NoTabs("abcdefghijklmnopqrst".into());
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "klmnopqrst");
-
- buf.clear();
- let style = ProgressStyle::with_template("{wide_msg:^}").unwrap();
- state.message = TabExpandedString::NoTabs("abcdefghijklmnopqrst".into());
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "fghijklmno");
- }
-
- #[test]
- fn wide_element_style() {
- const CHARS: &str = "=>-";
- const WIDTH: u16 = 8;
- let pos = Arc::new(AtomicPosition::new());
- // half finished
- pos.set(2);
- let mut state = ProgressState::new(Some(4), pos);
- let mut buf = Vec::new();
-
- let style = ProgressStyle::with_template("{wide_bar}")
- .unwrap()
- .progress_chars(CHARS);
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "====>---");
-
- buf.clear();
- let style = ProgressStyle::with_template("{wide_bar:.red.on_blue/green.on_cyan}")
- .unwrap()
- .progress_chars(CHARS);
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(
- &buf[0],
- "\u{1b}[31m\u{1b}[44m====>\u{1b}[32m\u{1b}[46m---\u{1b}[0m\u{1b}[0m"
- );
-
- buf.clear();
- let style = ProgressStyle::with_template("{wide_msg:^.red.on_blue}").unwrap();
- state.message = TabExpandedString::NoTabs("foobar".into());
- style.format_state(&state, &mut buf, WIDTH);
- assert_eq!(&buf[0], "\u{1b}[31m\u{1b}[44m foobar \u{1b}[0m");
- }
-
- #[test]
- fn multiline_handling() {
- const WIDTH: u16 = 80;
- let pos = Arc::new(AtomicPosition::new());
- let mut state = ProgressState::new(Some(10), pos);
- let mut buf = Vec::new();
-
- let mut style = ProgressStyle::default_bar();
- state.message = TabExpandedString::new("foo\nbar\nbaz".into(), 2);
- style.template = Template::from_str("{msg}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
-
- assert_eq!(buf.len(), 3);
- assert_eq!(&buf[0], "foo");
- assert_eq!(&buf[1], "bar");
- assert_eq!(&buf[2], "baz");
-
- buf.clear();
- style.template = Template::from_str("{wide_msg}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
-
- assert_eq!(buf.len(), 3);
- assert_eq!(&buf[0], "foo");
- assert_eq!(&buf[1], "bar");
- assert_eq!(&buf[2], "baz");
-
- buf.clear();
- state.prefix = TabExpandedString::new("prefix\nprefix".into(), 2);
- style.template = Template::from_str("{prefix} {wide_msg}").unwrap();
- style.format_state(&state, &mut buf, WIDTH);
-
- assert_eq!(buf.len(), 4);
- assert_eq!(&buf[0], "prefix");
- assert_eq!(&buf[1], "prefix foo");
- assert_eq!(&buf[2], "bar");
- assert_eq!(&buf[3], "baz");
- }
-}