diff options
Diffstat (limited to 'vendor/indicatif/src/style.rs')
-rw-r--r-- | vendor/indicatif/src/style.rs | 987 |
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"); - } -} |