diff options
author | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
commit | a990de90fe41456a23e58bd087d2f107d321f3a1 (patch) | |
tree | 15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/indicatif/src | |
parent | 3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff) | |
download | fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip |
Deleted vendor folder
Diffstat (limited to 'vendor/indicatif/src')
-rw-r--r-- | vendor/indicatif/src/draw_target.rs | 561 | ||||
-rw-r--r-- | vendor/indicatif/src/format.rs | 337 | ||||
-rw-r--r-- | vendor/indicatif/src/in_memory.rs | 399 | ||||
-rw-r--r-- | vendor/indicatif/src/iter.rs | 355 | ||||
-rw-r--r-- | vendor/indicatif/src/lib.rs | 247 | ||||
-rw-r--r-- | vendor/indicatif/src/multi.rs | 688 | ||||
-rw-r--r-- | vendor/indicatif/src/progress_bar.rs | 808 | ||||
-rw-r--r-- | vendor/indicatif/src/rayon.rs | 235 | ||||
-rw-r--r-- | vendor/indicatif/src/state.rs | 798 | ||||
-rw-r--r-- | vendor/indicatif/src/style.rs | 987 | ||||
-rw-r--r-- | vendor/indicatif/src/term_like.rs | 79 |
11 files changed, 0 insertions, 5494 deletions
diff --git a/vendor/indicatif/src/draw_target.rs b/vendor/indicatif/src/draw_target.rs deleted file mode 100644 index a2c055f..0000000 --- a/vendor/indicatif/src/draw_target.rs +++ /dev/null @@ -1,561 +0,0 @@ -use std::io; -use std::sync::{Arc, RwLock, RwLockWriteGuard}; -use std::thread::panicking; -use std::time::Duration; -#[cfg(not(target_arch = "wasm32"))] -use std::time::Instant; - -use console::Term; -#[cfg(target_arch = "wasm32")] -use instant::Instant; - -use crate::multi::{MultiProgressAlignment, MultiState}; -use crate::TermLike; - -/// Target for draw operations -/// -/// This tells a progress bar or a multi progress object where to paint to. -/// The draw target is a stateful wrapper over a drawing destination and -/// internally optimizes how often the state is painted to the output -/// device. -#[derive(Debug)] -pub struct ProgressDrawTarget { - kind: TargetKind, -} - -impl ProgressDrawTarget { - /// Draw to a buffered stdout terminal at a max of 20 times a second. - /// - /// For more information see [`ProgressDrawTarget::term`]. - pub fn stdout() -> Self { - Self::term(Term::buffered_stdout(), 20) - } - - /// Draw to a buffered stderr terminal at a max of 20 times a second. - /// - /// This is the default draw target for progress bars. For more - /// information see [`ProgressDrawTarget::term`]. - pub fn stderr() -> Self { - Self::term(Term::buffered_stderr(), 20) - } - - /// Draw to a buffered stdout terminal at a max of `refresh_rate` times a second. - /// - /// For more information see [`ProgressDrawTarget::term`]. - pub fn stdout_with_hz(refresh_rate: u8) -> Self { - Self::term(Term::buffered_stdout(), refresh_rate) - } - - /// Draw to a buffered stderr terminal at a max of `refresh_rate` times a second. - /// - /// For more information see [`ProgressDrawTarget::term`]. - pub fn stderr_with_hz(refresh_rate: u8) -> Self { - Self::term(Term::buffered_stderr(), refresh_rate) - } - - pub(crate) fn new_remote(state: Arc<RwLock<MultiState>>, idx: usize) -> Self { - Self { - kind: TargetKind::Multi { state, idx }, - } - } - - /// Draw to a terminal, with a specific refresh rate. - /// - /// Progress bars are by default drawn to terminals however if the - /// terminal is not user attended the entire progress bar will be - /// hidden. This is done so that piping to a file will not produce - /// useless escape codes in that file. - /// - /// Will panic if refresh_rate is `0`. - pub fn term(term: Term, refresh_rate: u8) -> Self { - Self { - kind: TargetKind::Term { - term, - last_line_count: 0, - rate_limiter: RateLimiter::new(refresh_rate), - draw_state: DrawState::default(), - }, - } - } - - /// Draw to a boxed object that implements the [`TermLike`] trait. - pub fn term_like(term_like: Box<dyn TermLike>) -> Self { - Self { - kind: TargetKind::TermLike { - inner: term_like, - last_line_count: 0, - rate_limiter: None, - draw_state: DrawState::default(), - }, - } - } - - /// Draw to a boxed object that implements the [`TermLike`] trait, - /// with a specific refresh rate. - pub fn term_like_with_hz(term_like: Box<dyn TermLike>, refresh_rate: u8) -> Self { - Self { - kind: TargetKind::TermLike { - inner: term_like, - last_line_count: 0, - rate_limiter: Option::from(RateLimiter::new(refresh_rate)), - draw_state: DrawState::default(), - }, - } - } - - /// A hidden draw target. - /// - /// This forces a progress bar to be not rendered at all. - pub fn hidden() -> Self { - Self { - kind: TargetKind::Hidden, - } - } - - /// Returns true if the draw target is hidden. - /// - /// This is internally used in progress bars to figure out if overhead - /// from drawing can be prevented. - pub fn is_hidden(&self) -> bool { - match self.kind { - TargetKind::Hidden => true, - TargetKind::Term { ref term, .. } => !term.is_term(), - TargetKind::Multi { ref state, .. } => state.read().unwrap().is_hidden(), - _ => false, - } - } - - /// Returns the current width of the draw target. - pub(crate) fn width(&self) -> u16 { - match self.kind { - TargetKind::Term { ref term, .. } => term.size().1, - TargetKind::Multi { ref state, .. } => state.read().unwrap().width(), - TargetKind::Hidden => 0, - TargetKind::TermLike { ref inner, .. } => inner.width(), - } - } - - /// Notifies the backing `MultiProgress` (if applicable) that the associated progress bar should - /// be marked a zombie. - pub(crate) fn mark_zombie(&self) { - if let TargetKind::Multi { idx, state } = &self.kind { - state.write().unwrap().mark_zombie(*idx); - } - } - - /// Apply the given draw state (draws it). - pub(crate) fn drawable(&mut self, force_draw: bool, now: Instant) -> Option<Drawable<'_>> { - match &mut self.kind { - TargetKind::Term { - term, - last_line_count, - rate_limiter, - draw_state, - } => { - if !term.is_term() { - return None; - } - - match force_draw || rate_limiter.allow(now) { - true => Some(Drawable::Term { - term, - last_line_count, - draw_state, - }), - false => None, // rate limited - } - } - TargetKind::Multi { idx, state, .. } => { - let state = state.write().unwrap(); - Some(Drawable::Multi { - idx: *idx, - state, - force_draw, - now, - }) - } - TargetKind::TermLike { - inner, - last_line_count, - rate_limiter, - draw_state, - } => match force_draw || rate_limiter.as_mut().map_or(true, |r| r.allow(now)) { - true => Some(Drawable::TermLike { - term_like: &**inner, - last_line_count, - draw_state, - }), - false => None, // rate limited - }, - // Hidden, finished, or no need to refresh yet - _ => None, - } - } - - /// Properly disconnects from the draw target - pub(crate) fn disconnect(&self, now: Instant) { - match self.kind { - TargetKind::Term { .. } => {} - TargetKind::Multi { idx, ref state, .. } => { - let state = state.write().unwrap(); - let _ = Drawable::Multi { - state, - idx, - force_draw: true, - now, - } - .clear(); - } - TargetKind::Hidden => {} - TargetKind::TermLike { .. } => {} - }; - } - - pub(crate) fn remote(&self) -> Option<(&Arc<RwLock<MultiState>>, usize)> { - match &self.kind { - TargetKind::Multi { state, idx } => Some((state, *idx)), - _ => None, - } - } - - pub(crate) fn adjust_last_line_count(&mut self, adjust: LineAdjust) { - self.kind.adjust_last_line_count(adjust); - } -} - -#[derive(Debug)] -enum TargetKind { - Term { - term: Term, - last_line_count: usize, - rate_limiter: RateLimiter, - draw_state: DrawState, - }, - Multi { - state: Arc<RwLock<MultiState>>, - idx: usize, - }, - Hidden, - TermLike { - inner: Box<dyn TermLike>, - last_line_count: usize, - rate_limiter: Option<RateLimiter>, - draw_state: DrawState, - }, -} - -impl TargetKind { - /// Adjust `last_line_count` such that the next draw operation keeps/clears additional lines - fn adjust_last_line_count(&mut self, adjust: LineAdjust) { - let last_line_count: &mut usize = match self { - Self::Term { - last_line_count, .. - } => last_line_count, - Self::TermLike { - last_line_count, .. - } => last_line_count, - _ => return, - }; - - match adjust { - LineAdjust::Clear(count) => *last_line_count = last_line_count.saturating_add(count), - LineAdjust::Keep(count) => *last_line_count = last_line_count.saturating_sub(count), - } - } -} - -pub(crate) enum Drawable<'a> { - Term { - term: &'a Term, - last_line_count: &'a mut usize, - draw_state: &'a mut DrawState, - }, - Multi { - state: RwLockWriteGuard<'a, MultiState>, - idx: usize, - force_draw: bool, - now: Instant, - }, - TermLike { - term_like: &'a dyn TermLike, - last_line_count: &'a mut usize, - draw_state: &'a mut DrawState, - }, -} - -impl<'a> Drawable<'a> { - /// Adjust `last_line_count` such that the next draw operation keeps/clears additional lines - pub(crate) fn adjust_last_line_count(&mut self, adjust: LineAdjust) { - let last_line_count: &mut usize = match self { - Drawable::Term { - last_line_count, .. - } => last_line_count, - Drawable::TermLike { - last_line_count, .. - } => last_line_count, - _ => return, - }; - - match adjust { - LineAdjust::Clear(count) => *last_line_count = last_line_count.saturating_add(count), - LineAdjust::Keep(count) => *last_line_count = last_line_count.saturating_sub(count), - } - } - - pub(crate) fn state(&mut self) -> DrawStateWrapper<'_> { - let mut state = match self { - Drawable::Term { draw_state, .. } => DrawStateWrapper::for_term(draw_state), - Drawable::Multi { state, idx, .. } => state.draw_state(*idx), - Drawable::TermLike { draw_state, .. } => DrawStateWrapper::for_term(draw_state), - }; - - state.reset(); - state - } - - pub(crate) fn clear(mut self) -> io::Result<()> { - let state = self.state(); - drop(state); - self.draw() - } - - pub(crate) fn draw(self) -> io::Result<()> { - match self { - Drawable::Term { - term, - last_line_count, - draw_state, - } => draw_state.draw_to_term(term, last_line_count), - Drawable::Multi { - mut state, - force_draw, - now, - .. - } => state.draw(force_draw, None, now), - Drawable::TermLike { - term_like, - last_line_count, - draw_state, - } => draw_state.draw_to_term(term_like, last_line_count), - } - } -} - -pub(crate) enum LineAdjust { - /// Adds to `last_line_count` so that the next draw also clears those lines - Clear(usize), - /// Subtracts from `last_line_count` so that the next draw retains those lines - Keep(usize), -} - -pub(crate) struct DrawStateWrapper<'a> { - state: &'a mut DrawState, - orphan_lines: Option<&'a mut Vec<String>>, -} - -impl<'a> DrawStateWrapper<'a> { - pub(crate) fn for_term(state: &'a mut DrawState) -> Self { - Self { - state, - orphan_lines: None, - } - } - - pub(crate) fn for_multi(state: &'a mut DrawState, orphan_lines: &'a mut Vec<String>) -> Self { - Self { - state, - orphan_lines: Some(orphan_lines), - } - } -} - -impl std::ops::Deref for DrawStateWrapper<'_> { - type Target = DrawState; - - fn deref(&self) -> &Self::Target { - self.state - } -} - -impl std::ops::DerefMut for DrawStateWrapper<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.state - } -} - -impl Drop for DrawStateWrapper<'_> { - fn drop(&mut self) { - if let Some(orphaned) = &mut self.orphan_lines { - orphaned.extend(self.state.lines.drain(..self.state.orphan_lines_count)); - self.state.orphan_lines_count = 0; - } - } -} - -#[derive(Debug)] -struct RateLimiter { - interval: u16, // in milliseconds - capacity: u8, - prev: Instant, -} - -/// Rate limit but allow occasional bursts above desired rate -impl RateLimiter { - fn new(rate: u8) -> Self { - Self { - interval: 1000 / (rate as u16), // between 3 and 1000 milliseconds - capacity: MAX_BURST, - prev: Instant::now(), - } - } - - fn allow(&mut self, now: Instant) -> bool { - if now < self.prev { - return false; - } - - let elapsed = now - self.prev; - // If `capacity` is 0 and not enough time (`self.interval` ms) has passed since - // `self.prev` to add new capacity, return `false`. The goal of this method is to - // make this decision as efficient as possible. - if self.capacity == 0 && elapsed < Duration::from_millis(self.interval as u64) { - return false; - } - - // We now calculate `new`, the number of ms, since we last returned `true`, - // and `remainder`, which represents a number of ns less than 1ms which we cannot - // convert into capacity now, so we're saving it for later. - let (new, remainder) = ( - elapsed.as_millis() / self.interval as u128, - elapsed.as_nanos() % (self.interval as u128 * 1_000_000), - ); - - // We add `new` to `capacity`, subtract one for returning `true` from here, - // then make sure it does not exceed a maximum of `MAX_BURST`, then store it. - self.capacity = Ord::min(MAX_BURST as u128, (self.capacity as u128) + new - 1) as u8; - // Store `prev` for the next iteration after subtracting the `remainder`. - // Just use `unwrap` here because it shouldn't be possible for this to underflow. - self.prev = now - .checked_sub(Duration::from_nanos(remainder as u64)) - .unwrap(); - true - } -} - -const MAX_BURST: u8 = 20; - -/// The drawn state of an element. -#[derive(Clone, Debug, Default)] -pub(crate) struct DrawState { - /// The lines to print (can contain ANSI codes) - pub(crate) lines: Vec<String>, - /// The number of lines that shouldn't be reaped by the next tick. - pub(crate) orphan_lines_count: usize, - /// True if we should move the cursor up when possible instead of clearing lines. - pub(crate) move_cursor: bool, - /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top` - pub(crate) alignment: MultiProgressAlignment, -} - -impl DrawState { - fn draw_to_term( - &mut self, - term: &(impl TermLike + ?Sized), - last_line_count: &mut usize, - ) -> io::Result<()> { - if panicking() { - return Ok(()); - } - - if !self.lines.is_empty() && self.move_cursor { - term.move_cursor_up(*last_line_count)?; - } else { - // Fork of console::clear_last_lines that assumes that the last line doesn't contain a '\n' - let n = *last_line_count; - term.move_cursor_up(n.saturating_sub(1))?; - for i in 0..n { - term.clear_line()?; - if i + 1 != n { - term.move_cursor_down(1)?; - } - } - term.move_cursor_up(n.saturating_sub(1))?; - } - - let shift = match self.alignment { - MultiProgressAlignment::Bottom if self.lines.len() < *last_line_count => { - let shift = *last_line_count - self.lines.len(); - for _ in 0..shift { - term.write_line("")?; - } - shift - } - _ => 0, - }; - - let term_height = term.height() as usize; - let term_width = term.width() as usize; - let len = self.lines.len(); - let mut real_len = 0; - let mut last_line_filler = 0; - debug_assert!(self.orphan_lines_count <= self.lines.len()); - for (idx, line) in self.lines.iter().enumerate() { - let line_width = console::measure_text_width(line); - let diff = if line.is_empty() { - // Empty line are new line - 1 - } else { - // Calculate real length based on terminal width - // This take in account linewrap from terminal - let terminal_len = (line_width as f64 / term_width as f64).ceil() as usize; - - // If the line is effectively empty (for example when it consists - // solely of ANSI color code sequences, count it the same as a - // new line. If the line is measured to be len = 0, we will - // subtract with overflow later. - usize::max(terminal_len, 1) - }; - // Don't consider orphan lines when comparing to terminal height. - debug_assert!(idx <= real_len); - if self.orphan_lines_count <= idx - && real_len - self.orphan_lines_count + diff > term_height - { - break; - } - real_len += diff; - if idx != 0 { - term.write_line("")?; - } - term.write_str(line)?; - if idx + 1 == len { - // Keep the cursor on the right terminal side - // So that next user writes/prints will happen on the next line - last_line_filler = term_width.saturating_sub(line_width); - } - } - term.write_str(&" ".repeat(last_line_filler))?; - - term.flush()?; - *last_line_count = real_len - self.orphan_lines_count + shift; - Ok(()) - } - - fn reset(&mut self) { - self.lines.clear(); - self.orphan_lines_count = 0; - } -} - -#[cfg(test)] -mod tests { - use crate::{MultiProgress, ProgressBar, ProgressDrawTarget}; - - #[test] - fn multi_is_hidden() { - let mp = MultiProgress::with_draw_target(ProgressDrawTarget::hidden()); - - let pb = mp.add(ProgressBar::new(100)); - assert!(mp.is_hidden()); - assert!(pb.is_hidden()); - } -} diff --git a/vendor/indicatif/src/format.rs b/vendor/indicatif/src/format.rs deleted file mode 100644 index 7475a25..0000000 --- a/vendor/indicatif/src/format.rs +++ /dev/null @@ -1,337 +0,0 @@ -use std::fmt; -use std::time::Duration; - -use number_prefix::NumberPrefix; - -const SECOND: Duration = Duration::from_secs(1); -const MINUTE: Duration = Duration::from_secs(60); -const HOUR: Duration = Duration::from_secs(60 * 60); -const DAY: Duration = Duration::from_secs(24 * 60 * 60); -const WEEK: Duration = Duration::from_secs(7 * 24 * 60 * 60); -const YEAR: Duration = Duration::from_secs(365 * 24 * 60 * 60); - -/// Wraps an std duration for human basic formatting. -#[derive(Debug)] -pub struct FormattedDuration(pub Duration); - -/// Wraps an std duration for human readable formatting. -#[derive(Debug)] -pub struct HumanDuration(pub Duration); - -/// Formats bytes for human readability -#[derive(Debug)] -pub struct HumanBytes(pub u64); - -/// Formats bytes for human readability using SI prefixes -#[derive(Debug)] -pub struct DecimalBytes(pub u64); - -/// Formats bytes for human readability using ISO/IEC prefixes -#[derive(Debug)] -pub struct BinaryBytes(pub u64); - -/// Formats counts for human readability using commas -#[derive(Debug)] -pub struct HumanCount(pub u64); - -/// Formats counts for human readability using commas for floats -#[derive(Debug)] -pub struct HumanFloatCount(pub f64); - -impl fmt::Display for FormattedDuration { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut t = self.0.as_secs(); - let seconds = t % 60; - t /= 60; - let minutes = t % 60; - t /= 60; - let hours = t % 24; - t /= 24; - if t > 0 { - let days = t; - write!(f, "{days}d {hours:02}:{minutes:02}:{seconds:02}") - } else { - write!(f, "{hours:02}:{minutes:02}:{seconds:02}") - } - } -} - -// `HumanDuration` should be as intuitively understandable as possible. -// So we want to round, not truncate: otherwise 1 hour and 59 minutes -// would display an ETA of "1 hour" which underestimates the time -// remaining by a factor 2. -// -// To make the precision more uniform, we avoid displaying "1 unit" -// (except for seconds), because it would be displayed for a relatively -// long duration compared to the unit itself. Instead, when we arrive -// around 1.5 unit, we change from "2 units" to the next smaller unit -// (e.g. "89 seconds"). -// -// Formally: -// * for n >= 2, we go from "n+1 units" to "n units" exactly at (n + 1/2) units -// * we switch from "2 units" to the next smaller unit at (1.5 unit minus half of the next smaller unit) - -impl fmt::Display for HumanDuration { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut idx = 0; - for (i, &(cur, _, _)) in UNITS.iter().enumerate() { - idx = i; - match UNITS.get(i + 1) { - Some(&next) if self.0.saturating_add(next.0 / 2) >= cur + cur / 2 => break, - _ => continue, - } - } - - let (unit, name, alt) = UNITS[idx]; - // FIXME when `div_duration_f64` is stable - let mut t = (self.0.as_secs_f64() / unit.as_secs_f64()).round() as usize; - if idx < UNITS.len() - 1 { - t = Ord::max(t, 2); - } - - match (f.alternate(), t) { - (true, _) => write!(f, "{t}{alt}"), - (false, 1) => write!(f, "{t} {name}"), - (false, _) => write!(f, "{t} {name}s"), - } - } -} - -const UNITS: &[(Duration, &str, &str)] = &[ - (YEAR, "year", "y"), - (WEEK, "week", "w"), - (DAY, "day", "d"), - (HOUR, "hour", "h"), - (MINUTE, "minute", "m"), - (SECOND, "second", "s"), -]; - -impl fmt::Display for HumanBytes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match NumberPrefix::binary(self.0 as f64) { - NumberPrefix::Standalone(number) => write!(f, "{number:.0} B"), - NumberPrefix::Prefixed(prefix, number) => write!(f, "{number:.2} {prefix}B"), - } - } -} - -impl fmt::Display for DecimalBytes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match NumberPrefix::decimal(self.0 as f64) { - NumberPrefix::Standalone(number) => write!(f, "{number:.0} B"), - NumberPrefix::Prefixed(prefix, number) => write!(f, "{number:.2} {prefix}B"), - } - } -} - -impl fmt::Display for BinaryBytes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match NumberPrefix::binary(self.0 as f64) { - NumberPrefix::Standalone(number) => write!(f, "{number:.0} B"), - NumberPrefix::Prefixed(prefix, number) => write!(f, "{number:.2} {prefix}B"), - } - } -} - -impl fmt::Display for HumanCount { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use fmt::Write; - - let num = self.0.to_string(); - let len = num.len(); - for (idx, c) in num.chars().enumerate() { - let pos = len - idx - 1; - f.write_char(c)?; - if pos > 0 && pos % 3 == 0 { - f.write_char(',')?; - } - } - Ok(()) - } -} - -impl fmt::Display for HumanFloatCount { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use fmt::Write; - - let num = format!("{:.4}", self.0); - let (int_part, frac_part) = match num.split_once('.') { - Some((int_str, fract_str)) => (int_str.to_string(), fract_str), - None => (self.0.trunc().to_string(), ""), - }; - let len = int_part.len(); - for (idx, c) in int_part.chars().enumerate() { - let pos = len - idx - 1; - f.write_char(c)?; - if pos > 0 && pos % 3 == 0 { - f.write_char(',')?; - } - } - let frac_trimmed = frac_part.trim_end_matches('0'); - if !frac_trimmed.is_empty() { - f.write_char('.')?; - f.write_str(frac_trimmed)?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const MILLI: Duration = Duration::from_millis(1); - - #[test] - fn human_duration_alternate() { - for (unit, _, alt) in UNITS { - assert_eq!(format!("2{alt}"), format!("{:#}", HumanDuration(2 * *unit))); - } - } - - #[test] - fn human_duration_less_than_one_second() { - assert_eq!( - "0 seconds", - format!("{}", HumanDuration(Duration::from_secs(0))) - ); - assert_eq!("0 seconds", format!("{}", HumanDuration(MILLI))); - assert_eq!("0 seconds", format!("{}", HumanDuration(499 * MILLI))); - assert_eq!("1 second", format!("{}", HumanDuration(500 * MILLI))); - assert_eq!("1 second", format!("{}", HumanDuration(999 * MILLI))); - } - - #[test] - fn human_duration_less_than_two_seconds() { - assert_eq!("1 second", format!("{}", HumanDuration(1499 * MILLI))); - assert_eq!("2 seconds", format!("{}", HumanDuration(1500 * MILLI))); - assert_eq!("2 seconds", format!("{}", HumanDuration(1999 * MILLI))); - } - - #[test] - fn human_duration_one_unit() { - assert_eq!("1 second", format!("{}", HumanDuration(SECOND))); - assert_eq!("60 seconds", format!("{}", HumanDuration(MINUTE))); - assert_eq!("60 minutes", format!("{}", HumanDuration(HOUR))); - assert_eq!("24 hours", format!("{}", HumanDuration(DAY))); - assert_eq!("7 days", format!("{}", HumanDuration(WEEK))); - assert_eq!("52 weeks", format!("{}", HumanDuration(YEAR))); - } - - #[test] - fn human_duration_less_than_one_and_a_half_unit() { - // this one is actually done at 1.5 unit - half of the next smaller unit - epsilon - // and should display the next smaller unit - let d = HumanDuration(MINUTE + MINUTE / 2 - SECOND / 2 - MILLI); - assert_eq!("89 seconds", format!("{d}")); - let d = HumanDuration(HOUR + HOUR / 2 - MINUTE / 2 - MILLI); - assert_eq!("89 minutes", format!("{d}")); - let d = HumanDuration(DAY + DAY / 2 - HOUR / 2 - MILLI); - assert_eq!("35 hours", format!("{d}")); - let d = HumanDuration(WEEK + WEEK / 2 - DAY / 2 - MILLI); - assert_eq!("10 days", format!("{d}")); - let d = HumanDuration(YEAR + YEAR / 2 - WEEK / 2 - MILLI); - assert_eq!("78 weeks", format!("{d}")); - } - - #[test] - fn human_duration_one_and_a_half_unit() { - // this one is actually done at 1.5 unit - half of the next smaller unit - // and should still display "2 units" - let d = HumanDuration(MINUTE + MINUTE / 2 - SECOND / 2); - assert_eq!("2 minutes", format!("{d}")); - let d = HumanDuration(HOUR + HOUR / 2 - MINUTE / 2); - assert_eq!("2 hours", format!("{d}")); - let d = HumanDuration(DAY + DAY / 2 - HOUR / 2); - assert_eq!("2 days", format!("{d}")); - let d = HumanDuration(WEEK + WEEK / 2 - DAY / 2); - assert_eq!("2 weeks", format!("{d}")); - let d = HumanDuration(YEAR + YEAR / 2 - WEEK / 2); - assert_eq!("2 years", format!("{d}")); - } - - #[test] - fn human_duration_two_units() { - assert_eq!("2 seconds", format!("{}", HumanDuration(2 * SECOND))); - assert_eq!("2 minutes", format!("{}", HumanDuration(2 * MINUTE))); - assert_eq!("2 hours", format!("{}", HumanDuration(2 * HOUR))); - assert_eq!("2 days", format!("{}", HumanDuration(2 * DAY))); - assert_eq!("2 weeks", format!("{}", HumanDuration(2 * WEEK))); - assert_eq!("2 years", format!("{}", HumanDuration(2 * YEAR))); - } - - #[test] - fn human_duration_less_than_two_and_a_half_units() { - let d = HumanDuration(2 * SECOND + SECOND / 2 - MILLI); - assert_eq!("2 seconds", format!("{d}")); - let d = HumanDuration(2 * MINUTE + MINUTE / 2 - MILLI); - assert_eq!("2 minutes", format!("{d}")); - let d = HumanDuration(2 * HOUR + HOUR / 2 - MILLI); - assert_eq!("2 hours", format!("{d}")); - let d = HumanDuration(2 * DAY + DAY / 2 - MILLI); - assert_eq!("2 days", format!("{d}")); - let d = HumanDuration(2 * WEEK + WEEK / 2 - MILLI); - assert_eq!("2 weeks", format!("{d}")); - let d = HumanDuration(2 * YEAR + YEAR / 2 - MILLI); - assert_eq!("2 years", format!("{d}")); - } - - #[test] - fn human_duration_two_and_a_half_units() { - let d = HumanDuration(2 * SECOND + SECOND / 2); - assert_eq!("3 seconds", format!("{d}")); - let d = HumanDuration(2 * MINUTE + MINUTE / 2); - assert_eq!("3 minutes", format!("{d}")); - let d = HumanDuration(2 * HOUR + HOUR / 2); - assert_eq!("3 hours", format!("{d}")); - let d = HumanDuration(2 * DAY + DAY / 2); - assert_eq!("3 days", format!("{d}")); - let d = HumanDuration(2 * WEEK + WEEK / 2); - assert_eq!("3 weeks", format!("{d}")); - let d = HumanDuration(2 * YEAR + YEAR / 2); - assert_eq!("3 years", format!("{d}")); - } - - #[test] - fn human_duration_three_units() { - assert_eq!("3 seconds", format!("{}", HumanDuration(3 * SECOND))); - assert_eq!("3 minutes", format!("{}", HumanDuration(3 * MINUTE))); - assert_eq!("3 hours", format!("{}", HumanDuration(3 * HOUR))); - assert_eq!("3 days", format!("{}", HumanDuration(3 * DAY))); - assert_eq!("3 weeks", format!("{}", HumanDuration(3 * WEEK))); - assert_eq!("3 years", format!("{}", HumanDuration(3 * YEAR))); - } - - #[test] - fn human_count() { - assert_eq!("42", format!("{}", HumanCount(42))); - assert_eq!("7,654", format!("{}", HumanCount(7654))); - assert_eq!("12,345", format!("{}", HumanCount(12345))); - assert_eq!("1,234,567,890", format!("{}", HumanCount(1234567890))); - } - - #[test] - fn human_float_count() { - assert_eq!("42", format!("{}", HumanFloatCount(42.0))); - assert_eq!("7,654", format!("{}", HumanFloatCount(7654.0))); - assert_eq!("12,345", format!("{}", HumanFloatCount(12345.0))); - assert_eq!( - "1,234,567,890", - format!("{}", HumanFloatCount(1234567890.0)) - ); - assert_eq!("42.5", format!("{}", HumanFloatCount(42.5))); - assert_eq!("42.5", format!("{}", HumanFloatCount(42.500012345))); - assert_eq!("42.502", format!("{}", HumanFloatCount(42.502012345))); - assert_eq!("7,654.321", format!("{}", HumanFloatCount(7654.321))); - assert_eq!("7,654.321", format!("{}", HumanFloatCount(7654.3210123456))); - assert_eq!("12,345.6789", format!("{}", HumanFloatCount(12345.6789))); - assert_eq!( - "1,234,567,890.1235", - format!("{}", HumanFloatCount(1234567890.1234567)) - ); - assert_eq!( - "1,234,567,890.1234", - format!("{}", HumanFloatCount(1234567890.1234321)) - ); - } -} diff --git a/vendor/indicatif/src/in_memory.rs b/vendor/indicatif/src/in_memory.rs deleted file mode 100644 index 046ae14..0000000 --- a/vendor/indicatif/src/in_memory.rs +++ /dev/null @@ -1,399 +0,0 @@ -use std::fmt::{Debug, Formatter, Write as _}; -use std::io::Write as _; -use std::sync::{Arc, Mutex}; - -use vt100::Parser; - -use crate::TermLike; - -/// A thin wrapper around [`vt100::Parser`]. -/// -/// This is just an [`Arc`] around its internal state, so it can be freely cloned. -#[cfg_attr(docsrs, doc(cfg(feature = "in_memory")))] -#[derive(Debug, Clone)] -pub struct InMemoryTerm { - state: Arc<Mutex<InMemoryTermState>>, -} - -impl InMemoryTerm { - pub fn new(rows: u16, cols: u16) -> InMemoryTerm { - assert!(rows > 0, "rows must be > 0"); - assert!(cols > 0, "cols must be > 0"); - InMemoryTerm { - state: Arc::new(Mutex::new(InMemoryTermState::new(rows, cols))), - } - } - - pub fn reset(&self) { - let mut state = self.state.lock().unwrap(); - *state = InMemoryTermState::new(state.height, state.width); - } - - pub fn contents(&self) -> String { - let state = self.state.lock().unwrap(); - - // For some reason, the `Screen::contents` method doesn't include newlines in what it - // returns, making it useless for our purposes. So we need to manually reconstruct the - // contents by iterating over the rows in the terminal buffer. - let mut rows = state - .parser - .screen() - .rows(0, state.width) - .collect::<Vec<_>>(); - - // Reverse the rows and trim empty lines from the end - rows = rows - .into_iter() - .rev() - .skip_while(|line| line.is_empty()) - .map(|line| line.trim_end().to_string()) - .collect(); - - // Un-reverse the rows and join them up with newlines - rows.reverse(); - rows.join("\n") - } - - pub fn contents_formatted(&self) -> Vec<u8> { - let state = self.state.lock().unwrap(); - - // For some reason, the `Screen::contents` method doesn't include newlines in what it - // returns, making it useless for our purposes. So we need to manually reconstruct the - // contents by iterating over the rows in the terminal buffer. - let mut rows = state - .parser - .screen() - .rows_formatted(0, state.width) - .collect::<Vec<_>>(); - - // Reverse the rows and trim empty lines from the end - rows = rows - .into_iter() - .rev() - .skip_while(|line| line.is_empty()) - .collect(); - - // Un-reverse the rows - rows.reverse(); - - // Calculate buffer size - let reset = b"[m"; - let len = rows.iter().map(|line| line.len() + reset.len() + 1).sum(); - - // Join rows up with reset codes and newlines - let mut contents = rows.iter().fold(Vec::with_capacity(len), |mut acc, cur| { - acc.extend_from_slice(cur); - acc.extend_from_slice(reset); - acc.push(b'\n'); - acc - }); - - // Remove last newline again, but leave the reset code - contents.truncate(len.saturating_sub(1)); - contents - } - - pub fn moves_since_last_check(&self) -> String { - let mut s = String::new(); - for line in std::mem::take(&mut self.state.lock().unwrap().history) { - writeln!(s, "{line:?}").unwrap(); - } - s - } -} - -impl TermLike for InMemoryTerm { - fn width(&self) -> u16 { - self.state.lock().unwrap().width - } - - fn height(&self) -> u16 { - self.state.lock().unwrap().height - } - - fn move_cursor_up(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Up(n)); - state.write_str(&format!("\x1b[{n}A")) - } - } - } - - fn move_cursor_down(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Down(n)); - state.write_str(&format!("\x1b[{n}B")) - } - } - } - - fn move_cursor_right(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Right(n)); - state.write_str(&format!("\x1b[{n}C")) - } - } - } - - fn move_cursor_left(&self, n: usize) -> std::io::Result<()> { - match n { - 0 => Ok(()), - _ => { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Left(n)); - state.write_str(&format!("\x1b[{n}D")) - } - } - } - - fn write_line(&self, s: &str) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Str(s.into())); - state.history.push(Move::NewLine); - - // Don't try to handle writing lines with additional newlines embedded in them - it's not - // worth the extra code for something that indicatif doesn't even do. May revisit in future. - debug_assert!( - s.lines().count() <= 1, - "calling write_line with embedded newlines is not allowed" - ); - - // vte100 needs the full \r\n sequence to jump to the next line and reset the cursor to - // the beginning of the line. Be flexible and take either \n or \r\n - state.write_str(s)?; - state.write_str("\r\n") - } - - fn write_str(&self, s: &str) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Str(s.into())); - state.write_str(s) - } - - fn clear_line(&self) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Clear); - state.write_str("\r\x1b[2K") - } - - fn flush(&self) -> std::io::Result<()> { - let mut state = self.state.lock().unwrap(); - state.history.push(Move::Flush); - state.parser.flush() - } -} - -struct InMemoryTermState { - width: u16, - height: u16, - parser: vt100::Parser, - history: Vec<Move>, -} - -impl InMemoryTermState { - pub(crate) fn new(rows: u16, cols: u16) -> InMemoryTermState { - InMemoryTermState { - width: cols, - height: rows, - parser: Parser::new(rows, cols, 0), - history: vec![], - } - } - - pub(crate) fn write_str(&mut self, s: &str) -> std::io::Result<()> { - self.parser.write_all(s.as_bytes()) - } -} - -impl Debug for InMemoryTermState { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("InMemoryTermState").finish_non_exhaustive() - } -} - -#[derive(Debug, PartialEq, Clone)] -enum Move { - Up(usize), - Down(usize), - Left(usize), - Right(usize), - Str(String), - NewLine, - Clear, - Flush, -} - -#[cfg(test)] -mod test { - use super::*; - - fn cursor_pos(in_mem: &InMemoryTerm) -> (u16, u16) { - in_mem - .state - .lock() - .unwrap() - .parser - .screen() - .cursor_position() - } - - #[test] - fn line_wrapping() { - let in_mem = InMemoryTerm::new(10, 5); - assert_eq!(cursor_pos(&in_mem), (0, 0)); - - in_mem.write_str("ABCDE").unwrap(); - assert_eq!(in_mem.contents(), "ABCDE"); - assert_eq!(cursor_pos(&in_mem), (0, 5)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("ABCDE") -"# - ); - - // Should wrap onto next line - in_mem.write_str("FG").unwrap(); - assert_eq!(in_mem.contents(), "ABCDE\nFG"); - assert_eq!(cursor_pos(&in_mem), (1, 2)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("FG") -"# - ); - - in_mem.write_str("HIJ").unwrap(); - assert_eq!(in_mem.contents(), "ABCDE\nFGHIJ"); - assert_eq!(cursor_pos(&in_mem), (1, 5)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("HIJ") -"# - ); - } - - #[test] - fn write_line() { - let in_mem = InMemoryTerm::new(10, 5); - assert_eq!(cursor_pos(&in_mem), (0, 0)); - - in_mem.write_line("A").unwrap(); - assert_eq!(in_mem.contents(), "A"); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("A") -NewLine -"# - ); - - in_mem.write_line("B").unwrap(); - assert_eq!(in_mem.contents(), "A\nB"); - assert_eq!(cursor_pos(&in_mem), (2, 0)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("B") -NewLine -"# - ); - - in_mem.write_line("Longer than cols").unwrap(); - assert_eq!(in_mem.contents(), "A\nB\nLonge\nr tha\nn col\ns"); - assert_eq!(cursor_pos(&in_mem), (6, 0)); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("Longer than cols") -NewLine -"# - ); - } - - #[test] - fn basic_functionality() { - let in_mem = InMemoryTerm::new(10, 80); - - in_mem.write_line("This is a test line").unwrap(); - assert_eq!(in_mem.contents(), "This is a test line"); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("This is a test line") -NewLine -"# - ); - - in_mem.write_line("And another line!").unwrap(); - assert_eq!(in_mem.contents(), "This is a test line\nAnd another line!"); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("And another line!") -NewLine -"# - ); - - in_mem.move_cursor_up(1).unwrap(); - in_mem.write_str("TEST").unwrap(); - - assert_eq!(in_mem.contents(), "This is a test line\nTESTanother line!"); - assert_eq!( - in_mem.moves_since_last_check(), - r#"Up(1) -Str("TEST") -"# - ); - } - - #[test] - fn newlines() { - let in_mem = InMemoryTerm::new(10, 10); - in_mem.write_line("LINE ONE").unwrap(); - in_mem.write_line("LINE TWO").unwrap(); - in_mem.write_line("").unwrap(); - in_mem.write_line("LINE FOUR").unwrap(); - - assert_eq!(in_mem.contents(), "LINE ONE\nLINE TWO\n\nLINE FOUR"); - - assert_eq!( - in_mem.moves_since_last_check(), - r#"Str("LINE ONE") -NewLine -Str("LINE TWO") -NewLine -Str("") -NewLine -Str("LINE FOUR") -NewLine -"# - ); - } - - #[test] - fn cursor_zero_movement() { - let in_mem = InMemoryTerm::new(10, 80); - in_mem.write_line("LINE ONE").unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - - // Check that moving zero rows/cols does not actually move cursor - in_mem.move_cursor_up(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - - in_mem.move_cursor_down(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 0)); - - in_mem.move_cursor_right(1).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 1)); - - in_mem.move_cursor_left(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 1)); - - in_mem.move_cursor_right(0).unwrap(); - assert_eq!(cursor_pos(&in_mem), (1, 1)); - } -} diff --git a/vendor/indicatif/src/iter.rs b/vendor/indicatif/src/iter.rs deleted file mode 100644 index 3e73660..0000000 --- a/vendor/indicatif/src/iter.rs +++ /dev/null @@ -1,355 +0,0 @@ -use std::borrow::Cow; -use std::io::{self, IoSliceMut}; -use std::iter::FusedIterator; -#[cfg(feature = "tokio")] -use std::pin::Pin; -#[cfg(feature = "tokio")] -use std::task::{Context, Poll}; -use std::time::Duration; - -#[cfg(feature = "tokio")] -use tokio::io::{ReadBuf, SeekFrom}; - -use crate::progress_bar::ProgressBar; -use crate::state::ProgressFinish; -use crate::style::ProgressStyle; - -/// Wraps an iterator to display its progress. -pub trait ProgressIterator -where - Self: Sized + Iterator, -{ - /// Wrap an iterator with default styling. Uses `Iterator::size_hint` to get length. - /// Returns `Some(..)` only if `size_hint.1` is `Some`. If you want to create a progress bar - /// even if `size_hint.1` returns `None` use `progress_count` or `progress_with` instead. - fn try_progress(self) -> Option<ProgressBarIter<Self>> { - self.size_hint() - .1 - .map(|len| self.progress_count(u64::try_from(len).unwrap())) - } - - /// Wrap an iterator with default styling. - fn progress(self) -> ProgressBarIter<Self> - where - Self: ExactSizeIterator, - { - let len = u64::try_from(self.len()).unwrap(); - self.progress_count(len) - } - - /// Wrap an iterator with an explicit element count. - fn progress_count(self, len: u64) -> ProgressBarIter<Self> { - self.progress_with(ProgressBar::new(len)) - } - - /// Wrap an iterator with a custom progress bar. - fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self>; - - /// Wrap an iterator with a progress bar and style it. - fn progress_with_style(self, style: crate::ProgressStyle) -> ProgressBarIter<Self> - where - Self: ExactSizeIterator, - { - let len = u64::try_from(self.len()).unwrap(); - let bar = ProgressBar::new(len).with_style(style); - self.progress_with(bar) - } -} - -/// Wraps an iterator to display its progress. -#[derive(Debug)] -pub struct ProgressBarIter<T> { - pub(crate) it: T, - pub progress: ProgressBar, -} - -impl<T> ProgressBarIter<T> { - /// Builder-like function for setting underlying progress bar's style. - /// - /// See [ProgressBar::with_style]. - pub fn with_style(mut self, style: ProgressStyle) -> Self { - self.progress = self.progress.with_style(style); - self - } - - /// Builder-like function for setting underlying progress bar's prefix. - /// - /// See [ProgressBar::with_prefix]. - pub fn with_prefix(mut self, prefix: impl Into<Cow<'static, str>>) -> Self { - self.progress = self.progress.with_prefix(prefix); - self - } - - /// Builder-like function for setting underlying progress bar's message. - /// - /// See [ProgressBar::with_message]. - pub fn with_message(mut self, message: impl Into<Cow<'static, str>>) -> Self { - self.progress = self.progress.with_message(message); - self - } - - /// Builder-like function for setting underlying progress bar's position. - /// - /// See [ProgressBar::with_position]. - pub fn with_position(mut self, position: u64) -> Self { - self.progress = self.progress.with_position(position); - self - } - - /// Builder-like function for setting underlying progress bar's elapsed time. - /// - /// See [ProgressBar::with_elapsed]. - pub fn with_elapsed(mut self, elapsed: Duration) -> Self { - self.progress = self.progress.with_elapsed(elapsed); - self - } - - /// Builder-like function for setting underlying progress bar's finish behavior. - /// - /// See [ProgressBar::with_finish]. - pub fn with_finish(mut self, finish: ProgressFinish) -> Self { - self.progress = self.progress.with_finish(finish); - self - } -} - -impl<S, T: Iterator<Item = S>> Iterator for ProgressBarIter<T> { - type Item = S; - - fn next(&mut self) -> Option<Self::Item> { - let item = self.it.next(); - - if item.is_some() { - self.progress.inc(1); - } else if !self.progress.is_finished() { - self.progress.finish_using_style(); - } - - item - } -} - -impl<T: ExactSizeIterator> ExactSizeIterator for ProgressBarIter<T> { - fn len(&self) -> usize { - self.it.len() - } -} - -impl<T: DoubleEndedIterator> DoubleEndedIterator for ProgressBarIter<T> { - fn next_back(&mut self) -> Option<Self::Item> { - let item = self.it.next_back(); - - if item.is_some() { - self.progress.inc(1); - } else if !self.progress.is_finished() { - self.progress.finish_using_style(); - } - - item - } -} - -impl<T: FusedIterator> FusedIterator for ProgressBarIter<T> {} - -impl<R: io::Read> io::Read for ProgressBarIter<R> { - fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { - let inc = self.it.read(buf)?; - self.progress.inc(inc as u64); - Ok(inc) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { - let inc = self.it.read_vectored(bufs)?; - self.progress.inc(inc as u64); - Ok(inc) - } - - fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { - let inc = self.it.read_to_string(buf)?; - self.progress.inc(inc as u64); - Ok(inc) - } - - fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { - self.it.read_exact(buf)?; - self.progress.inc(buf.len() as u64); - Ok(()) - } -} - -impl<R: io::BufRead> io::BufRead for ProgressBarIter<R> { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - self.it.fill_buf() - } - - fn consume(&mut self, amt: usize) { - self.it.consume(amt); - self.progress.inc(amt as u64); - } -} - -impl<S: io::Seek> io::Seek for ProgressBarIter<S> { - fn seek(&mut self, f: io::SeekFrom) -> io::Result<u64> { - self.it.seek(f).map(|pos| { - self.progress.set_position(pos); - pos - }) - } - // Pass this through to preserve optimizations that the inner I/O object may use here - // Also avoid sending a set_position update when the position hasn't changed - fn stream_position(&mut self) -> io::Result<u64> { - self.it.stream_position() - } -} - -#[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl<W: tokio::io::AsyncWrite + Unpin> tokio::io::AsyncWrite for ProgressBarIter<W> { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll<io::Result<usize>> { - Pin::new(&mut self.it).poll_write(cx, buf).map(|poll| { - poll.map(|inc| { - self.progress.inc(inc as u64); - inc - }) - }) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { - Pin::new(&mut self.it).poll_flush(cx) - } - - fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { - Pin::new(&mut self.it).poll_shutdown(cx) - } -} - -#[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl<W: tokio::io::AsyncRead + Unpin> tokio::io::AsyncRead for ProgressBarIter<W> { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll<io::Result<()>> { - let prev_len = buf.filled().len() as u64; - if let Poll::Ready(e) = Pin::new(&mut self.it).poll_read(cx, buf) { - self.progress.inc(buf.filled().len() as u64 - prev_len); - Poll::Ready(e) - } else { - Poll::Pending - } - } -} - -#[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl<W: tokio::io::AsyncSeek + Unpin> tokio::io::AsyncSeek for ProgressBarIter<W> { - fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> { - Pin::new(&mut self.it).start_seek(position) - } - - fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> { - Pin::new(&mut self.it).poll_complete(cx) - } -} - -#[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl<W: tokio::io::AsyncBufRead + Unpin + tokio::io::AsyncRead> tokio::io::AsyncBufRead - for ProgressBarIter<W> -{ - fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { - let this = self.get_mut(); - let result = Pin::new(&mut this.it).poll_fill_buf(cx); - if let Poll::Ready(Ok(buf)) = &result { - this.progress.inc(buf.len() as u64); - } - result - } - - fn consume(mut self: Pin<&mut Self>, amt: usize) { - Pin::new(&mut self.it).consume(amt); - } -} - -#[cfg(feature = "futures")] -#[cfg_attr(docsrs, doc(cfg(feature = "futures")))] -impl<S: futures_core::Stream + Unpin> futures_core::Stream for ProgressBarIter<S> { - type Item = S::Item; - - fn poll_next( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll<Option<Self::Item>> { - let this = self.get_mut(); - let item = std::pin::Pin::new(&mut this.it).poll_next(cx); - match &item { - std::task::Poll::Ready(Some(_)) => this.progress.inc(1), - std::task::Poll::Ready(None) => this.progress.finish_using_style(), - std::task::Poll::Pending => {} - } - item - } -} - -impl<W: io::Write> io::Write for ProgressBarIter<W> { - fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - self.it.write(buf).map(|inc| { - self.progress.inc(inc as u64); - inc - }) - } - - fn write_vectored(&mut self, bufs: &[io::IoSlice]) -> io::Result<usize> { - self.it.write_vectored(bufs).map(|inc| { - self.progress.inc(inc as u64); - inc - }) - } - - fn flush(&mut self) -> io::Result<()> { - self.it.flush() - } - - // write_fmt can not be captured with reasonable effort. - // as it uses write_all internally by default that should not be a problem. - // fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()>; -} - -impl<S, T: Iterator<Item = S>> ProgressIterator for T { - fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self> { - ProgressBarIter { it: self, progress } - } -} - -#[cfg(test)] -mod test { - use crate::iter::{ProgressBarIter, ProgressIterator}; - use crate::progress_bar::ProgressBar; - use crate::ProgressStyle; - - #[test] - fn it_can_wrap_an_iterator() { - let v = [1, 2, 3]; - let wrap = |it: ProgressBarIter<_>| { - assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]); - }; - - wrap(v.iter().progress()); - wrap(v.iter().progress_count(3)); - wrap({ - let pb = ProgressBar::new(v.len() as u64); - v.iter().progress_with(pb) - }); - wrap({ - let style = ProgressStyle::default_bar() - .template("{wide_bar:.red} {percent}/100%") - .unwrap(); - v.iter().progress_with_style(style) - }); - } -} diff --git a/vendor/indicatif/src/lib.rs b/vendor/indicatif/src/lib.rs deleted file mode 100644 index 6902ef6..0000000 --- a/vendor/indicatif/src/lib.rs +++ /dev/null @@ -1,247 +0,0 @@ -//! indicatif is a library for Rust that helps you build command line -//! interfaces that report progress to users. It comes with various -//! tools and utilities for formatting anything that indicates progress. -//! -//! Platform support: -//! -//! * Linux -//! * macOS -//! * Windows (colors require Windows 10) -//! -//! Best paired with other libraries in the family: -//! -//! * [console](https://docs.rs/console) -//! * [dialoguer](https://docs.rs/dialoguer) -//! -//! # Crate Contents -//! -//! * **Progress bars** -//! * [`ProgressBar`](struct.ProgressBar.html) for bars and spinners -//! * [`MultiProgress`](struct.MultiProgress.html) for multiple bars -//! * **Data Formatting** -//! * [`HumanBytes`](struct.HumanBytes.html) for formatting bytes -//! * [`DecimalBytes`](struct.DecimalBytes.html) for formatting bytes using SI prefixes -//! * [`BinaryBytes`](struct.BinaryBytes.html) for formatting bytes using ISO/IEC prefixes -//! * [`HumanDuration`](struct.HumanDuration.html) for formatting durations -//! * [`HumanCount`](struct.HumanCount.html) for formatting large counts -//! * [`HumanFloatCount`](struct.HumanFloatCount.html) for formatting large float counts -//! -//! # Progress Bars and Spinners -//! -//! indicatif comes with a `ProgressBar` type that supports both bounded -//! progress bar uses as well as unbounded "spinner" type progress reports. -//! Progress bars are `Sync` and `Send` objects which means that they are -//! internally locked and can be passed from thread to thread. -//! -//! Additionally a `MultiProgress` utility is provided that can manage -//! rendering multiple progress bars at once (eg: from multiple threads). -//! -//! To whet your appetite, this is what this can look like: -//! -//! <img src="https://github.com/console-rs/indicatif/raw/main/screenshots/yarn.gif?raw=true" width="60%"> -//! -//! Progress bars are manually advanced and by default draw to stderr. -//! When you are done, the progress bar can be finished either visibly -//! (eg: the progress bar stays on the screen) or cleared (the progress -//! bar will be removed). -//! -//! ```rust -//! use indicatif::ProgressBar; -//! -//! let bar = ProgressBar::new(1000); -//! for _ in 0..1000 { -//! bar.inc(1); -//! // ... -//! } -//! bar.finish(); -//! ``` -//! -//! General progress bar behaviors: -//! -//! * if a non terminal is detected the progress bar will be completely -//! hidden. This makes piping programs to logfiles make sense out of -//! the box. -//! * a progress bar only starts drawing when `set_message`, `inc`, `set_position` -//! or `tick` are called. In some situations you might have to call `tick` -//! once to draw it. -//! * progress bars should be explicitly finished to reset the rendering -//! for others. Either by also clearing them or by replacing them with -//! a new message / retaining the current message. -//! * the default template renders neither message nor prefix. -//! -//! # Iterators -//! -//! Similar to [tqdm](https://github.com/tqdm/tqdm), progress bars can be -//! associated with an iterator. For example: -//! -//! ```rust -//! use indicatif::ProgressIterator; -//! -//! for _ in (0..1000).progress() { -//! // ... -//! } -//! ``` -//! -//! See the [`ProgressIterator`](trait.ProgressIterator.html) trait for more -//! methods to configure the number of elements in the iterator or change -//! the progress bar style. Indicatif also has optional support for parallel -//! iterators with [Rayon](https://github.com/rayon-rs/rayon). In your -//! `Cargo.toml`, use the "rayon" feature: -//! -//! ```toml -//! [dependencies] -//! indicatif = {version = "*", features = ["rayon"]} -//! ``` -//! -//! And then use it like this: -//! -//! ```rust,ignore -//! # extern crate rayon; -//! use indicatif::ParallelProgressIterator; -//! use rayon::iter::{ParallelIterator, IntoParallelRefIterator}; -//! -//! let v: Vec<_> = (0..100000).collect(); -//! let v2: Vec<_> = v.par_iter().progress_count(v.len() as u64).map(|i| i + 1).collect(); -//! assert_eq!(v2[0], 1); -//! ``` -//! -//! Or if you'd like to customize the progress bar: -//! -//! ```rust,ignore -//! # extern crate rayon; -//! use indicatif::{ProgressBar, ParallelProgressIterator, ProgressStyle}; -//! use rayon::iter::{ParallelIterator, IntoParallelRefIterator}; -//! -//! // Alternatively, use `ProgressBar::new().with_style()` -//! let style = ProgressStyle::default_bar(); -//! let v: Vec<_> = (0..100000).collect(); -//! let v2: Vec<_> = v.par_iter().progress_with_style(style).map(|i| i + 1).collect(); -//! assert_eq!(v2[0], 1); -//! ``` -//! -//! # Templates -//! -//! Progress bars can be styled with simple format strings similar to the -//! ones in Rust itself. The format for a placeholder is `{key:options}` -//! where the `options` part is optional. If provided the format is this: -//! -//! ```text -//! <^> for an optional alignment specification (left, center and right respectively) -//! WIDTH an optional width as positive integer -//! ! an optional exclamation mark to enable truncation -//! .STYLE an optional dot separated style string -//! /STYLE an optional dot separated alternative style string -//! ``` -//! -//! For the style component see [`Style::from_dotted_str`](https://docs.rs/console/0.7.5/console/struct.Style.html#method.from_dotted_str) -//! for more information. Indicatif uses the `console` base crate for all -//! colorization and formatting options. -//! -//! Some examples for templates: -//! -//! ```text -//! [{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg} -//! ``` -//! -//! This sets a progress bar that is 40 characters wide and has cyan -//! as primary style color and blue as alternative style color. -//! Alternative styles are currently only used for progress bars. -//! -//! Example configuration: -//! -//! ```rust -//! # use indicatif::{ProgressBar, ProgressStyle}; -//! # let bar = ProgressBar::new(0); -//! bar.set_style(ProgressStyle::with_template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}") -//! .unwrap() -//! .progress_chars("##-")); -//! ``` -//! -//! The following keys exist: -//! -//! * `bar`: renders a progress bar. By default 20 characters wide. The -//! style string is used to color the elapsed part, the alternative -//! style is used for the bar that is yet to render. -//! * `wide_bar`: like `bar` but always fills the remaining space. It should not be used with -//! `wide_msg`. -//! * `spinner`: renders the spinner (current tick string). -//! * `prefix`: renders the prefix set on the progress bar. -//! * `msg`: renders the currently set message on the progress bar. -//! * `wide_msg`: like `msg` but always fills the remaining space and truncates. It should not be used -//! with `wide_bar`. -//! * `pos`: renders the current position of the bar as integer -//! * `human_pos`: renders the current position of the bar as an integer, with commas as the -//! thousands separator. -//! * `len`: renders the amount of work to be done as an integer -//! * `human_len`: renders the total length of the bar as an integer, with commas as the thousands -//! separator. -//! * `bytes`: renders the current position of the bar as bytes. -//! * `percent`: renders the current position of the bar as a percentage of the total length. -//! * `total_bytes`: renders the total length of the bar as bytes. -//! * `elapsed_precise`: renders the elapsed time as `HH:MM:SS`. -//! * `elapsed`: renders the elapsed time as `42s`, `1m` etc. -//! * `per_sec`: renders the speed in steps per second. -//! * `bytes_per_sec`: renders the speed in bytes per second. -//! * `binary_bytes_per_sec`: renders the speed in bytes per second using -//! power-of-two units, i.e. `MiB`, `KiB`, etc. -//! * `eta_precise`: the remaining time (like `elapsed_precise`). -//! * `eta`: the remaining time (like `elapsed`). -//! * `duration_precise`: the extrapolated total duration (like `elapsed_precise`). -//! * `duration`: the extrapolated total duration time (like `elapsed`). - -//! -//! The design of the progress bar can be altered with the integrated -//! template functionality. The template can be set by changing a -//! `ProgressStyle` and attaching it to the progress bar. -//! -//! # Human Readable Formatting -//! -//! There are some formatting wrappers for showing elapsed time and -//! file sizes for human users: -//! -//! ```rust -//! # use std::time::Duration; -//! use indicatif::{HumanBytes, HumanCount, HumanDuration, HumanFloatCount}; -//! -//! assert_eq!("3.00 MiB", HumanBytes(3*1024*1024).to_string()); -//! assert_eq!("8 seconds", HumanDuration(Duration::from_secs(8)).to_string()); -//! assert_eq!("33,857,009", HumanCount(33857009).to_string()); -//! assert_eq!("33,857,009.1235", HumanFloatCount(33857009.123456).to_string()); -//! ``` -//! -//! # Feature Flags -//! -//! * `rayon`: adds rayon support -//! * `improved_unicode`: adds improved unicode support (graphemes, better width calculation) - -#![cfg_attr(docsrs, feature(doc_cfg))] -#![warn(unreachable_pub)] - -mod draw_target; -mod format; -#[cfg(feature = "in_memory")] -mod in_memory; -mod iter; -mod multi; -mod progress_bar; -#[cfg(feature = "rayon")] -mod rayon; -mod state; -pub mod style; -mod term_like; - -pub use crate::draw_target::ProgressDrawTarget; -pub use crate::format::{ - BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanCount, HumanDuration, - HumanFloatCount, -}; -#[cfg(feature = "in_memory")] -pub use crate::in_memory::InMemoryTerm; -pub use crate::iter::{ProgressBarIter, ProgressIterator}; -pub use crate::multi::{MultiProgress, MultiProgressAlignment}; -pub use crate::progress_bar::{ProgressBar, WeakProgressBar}; -#[cfg(feature = "rayon")] -pub use crate::rayon::ParallelProgressIterator; -pub use crate::state::{ProgressFinish, ProgressState}; -pub use crate::style::ProgressStyle; -pub use crate::term_like::TermLike; diff --git a/vendor/indicatif/src/multi.rs b/vendor/indicatif/src/multi.rs deleted file mode 100644 index 4409309..0000000 --- a/vendor/indicatif/src/multi.rs +++ /dev/null @@ -1,688 +0,0 @@ -use std::fmt::{Debug, Formatter}; -use std::io; -use std::sync::{Arc, RwLock}; -use std::thread::panicking; -#[cfg(not(target_arch = "wasm32"))] -use std::time::Instant; - -use crate::draw_target::{DrawState, DrawStateWrapper, LineAdjust, ProgressDrawTarget}; -use crate::progress_bar::ProgressBar; -#[cfg(target_arch = "wasm32")] -use instant::Instant; - -/// Manages multiple progress bars from different threads -#[derive(Debug, Clone)] -pub struct MultiProgress { - pub(crate) state: Arc<RwLock<MultiState>>, -} - -impl Default for MultiProgress { - fn default() -> Self { - Self::with_draw_target(ProgressDrawTarget::stderr()) - } -} - -impl MultiProgress { - /// Creates a new multi progress object. - /// - /// Progress bars added to this object by default draw directly to stderr, and refresh - /// a maximum of 15 times a second. To change the refresh rate set the draw target to - /// one with a different refresh rate. - pub fn new() -> Self { - Self::default() - } - - /// Creates a new multi progress object with the given draw target. - pub fn with_draw_target(draw_target: ProgressDrawTarget) -> Self { - Self { - state: Arc::new(RwLock::new(MultiState::new(draw_target))), - } - } - - /// Sets a different draw target for the multiprogress bar. - pub fn set_draw_target(&self, target: ProgressDrawTarget) { - let mut state = self.state.write().unwrap(); - state.draw_target.disconnect(Instant::now()); - state.draw_target = target; - } - - /// Set whether we should try to move the cursor when possible instead of clearing lines. - /// - /// This can reduce flickering, but do not enable it if you intend to change the number of - /// progress bars. - pub fn set_move_cursor(&self, move_cursor: bool) { - self.state.write().unwrap().move_cursor = move_cursor; - } - - /// Set alignment flag - pub fn set_alignment(&self, alignment: MultiProgressAlignment) { - self.state.write().unwrap().alignment = alignment; - } - - /// Adds a progress bar. - /// - /// The progress bar added will have the draw target changed to a - /// remote draw target that is intercepted by the multi progress - /// object overriding custom `ProgressDrawTarget` settings. - /// - /// Adding a progress bar that is already a member of the `MultiProgress` - /// will have no effect. - pub fn add(&self, pb: ProgressBar) -> ProgressBar { - self.internalize(InsertLocation::End, pb) - } - - /// Inserts a progress bar. - /// - /// The progress bar inserted at position `index` will have the draw - /// target changed to a remote draw target that is intercepted by the - /// multi progress object overriding custom `ProgressDrawTarget` settings. - /// - /// If `index >= MultiProgressState::objects.len()`, the progress bar - /// is added to the end of the list. - /// - /// Inserting a progress bar that is already a member of the `MultiProgress` - /// will have no effect. - pub fn insert(&self, index: usize, pb: ProgressBar) -> ProgressBar { - self.internalize(InsertLocation::Index(index), pb) - } - - /// Inserts a progress bar from the back. - /// - /// The progress bar inserted at position `MultiProgressState::objects.len() - index` - /// will have the draw target changed to a remote draw target that is - /// intercepted by the multi progress object overriding custom - /// `ProgressDrawTarget` settings. - /// - /// If `index >= MultiProgressState::objects.len()`, the progress bar - /// is added to the start of the list. - /// - /// Inserting a progress bar that is already a member of the `MultiProgress` - /// will have no effect. - pub fn insert_from_back(&self, index: usize, pb: ProgressBar) -> ProgressBar { - self.internalize(InsertLocation::IndexFromBack(index), pb) - } - - /// Inserts a progress bar before an existing one. - /// - /// The progress bar added will have the draw target changed to a - /// remote draw target that is intercepted by the multi progress - /// object overriding custom `ProgressDrawTarget` settings. - /// - /// Inserting a progress bar that is already a member of the `MultiProgress` - /// will have no effect. - pub fn insert_before(&self, before: &ProgressBar, pb: ProgressBar) -> ProgressBar { - self.internalize(InsertLocation::Before(before.index().unwrap()), pb) - } - - /// Inserts a progress bar after an existing one. - /// - /// The progress bar added will have the draw target changed to a - /// remote draw target that is intercepted by the multi progress - /// object overriding custom `ProgressDrawTarget` settings. - /// - /// Inserting a progress bar that is already a member of the `MultiProgress` - /// will have no effect. - pub fn insert_after(&self, after: &ProgressBar, pb: ProgressBar) -> ProgressBar { - self.internalize(InsertLocation::After(after.index().unwrap()), pb) - } - - /// Removes a progress bar. - /// - /// The progress bar is removed only if it was previously inserted or added - /// by the methods `MultiProgress::insert` or `MultiProgress::add`. - /// If the passed progress bar does not satisfy the condition above, - /// the `remove` method does nothing. - pub fn remove(&self, pb: &ProgressBar) { - let mut state = pb.state(); - let idx = match &state.draw_target.remote() { - Some((state, idx)) => { - // Check that this progress bar is owned by the current MultiProgress. - assert!(Arc::ptr_eq(&self.state, state)); - *idx - } - _ => return, - }; - - state.draw_target = ProgressDrawTarget::hidden(); - self.state.write().unwrap().remove_idx(idx); - } - - fn internalize(&self, location: InsertLocation, pb: ProgressBar) -> ProgressBar { - let mut state = self.state.write().unwrap(); - let idx = state.insert(location); - drop(state); - - pb.set_draw_target(ProgressDrawTarget::new_remote(self.state.clone(), idx)); - pb - } - - /// Print a log line above all progress bars in the [`MultiProgress`] - /// - /// If the draw target is hidden (e.g. when standard output is not a terminal), `println()` - /// will not do anything. - pub fn println<I: AsRef<str>>(&self, msg: I) -> io::Result<()> { - let mut state = self.state.write().unwrap(); - state.println(msg, Instant::now()) - } - - /// Hide all progress bars temporarily, execute `f`, then redraw the [`MultiProgress`] - /// - /// Executes 'f' even if the draw target is hidden. - /// - /// Useful for external code that writes to the standard output. - /// - /// **Note:** The internal lock is held while `f` is executed. Other threads trying to print - /// anything on the progress bar will be blocked until `f` finishes. - /// Therefore, it is recommended to avoid long-running operations in `f`. - pub fn suspend<F: FnOnce() -> R, R>(&self, f: F) -> R { - let mut state = self.state.write().unwrap(); - state.suspend(f, Instant::now()) - } - - pub fn clear(&self) -> io::Result<()> { - self.state.write().unwrap().clear(Instant::now()) - } - - pub fn is_hidden(&self) -> bool { - self.state.read().unwrap().is_hidden() - } -} - -#[derive(Debug)] -pub(crate) struct MultiState { - /// The collection of states corresponding to progress bars - members: Vec<MultiStateMember>, - /// Set of removed bars, should have corresponding members in the `members` vector with a - /// `draw_state` of `None`. - free_set: Vec<usize>, - /// Indices to the `draw_states` to maintain correct visual order - ordering: Vec<usize>, - /// Target for draw operation for MultiProgress - draw_target: ProgressDrawTarget, - /// Whether or not to just move cursor instead of clearing lines - move_cursor: bool, - /// Controls how the multi progress is aligned if some of its progress bars get removed, default is `Top` - alignment: MultiProgressAlignment, - /// Lines to be drawn above everything else in the MultiProgress. These specifically come from - /// calling `ProgressBar::println` on a pb that is connected to a `MultiProgress`. - orphan_lines: Vec<String>, - /// The count of currently visible zombie lines. - zombie_lines_count: usize, -} - -impl MultiState { - fn new(draw_target: ProgressDrawTarget) -> Self { - Self { - members: vec![], - free_set: vec![], - ordering: vec![], - draw_target, - move_cursor: false, - alignment: MultiProgressAlignment::default(), - orphan_lines: Vec::new(), - zombie_lines_count: 0, - } - } - - pub(crate) fn mark_zombie(&mut self, index: usize) { - let member = &mut self.members[index]; - - // If the zombie is the first visual bar then we can reap it right now instead of - // deferring it to the next draw. - if index != self.ordering.first().copied().unwrap() { - member.is_zombie = true; - return; - } - - let line_count = member - .draw_state - .as_ref() - .map(|d| d.lines.len()) - .unwrap_or_default(); - - // Track the total number of zombie lines on the screen - self.zombie_lines_count = self.zombie_lines_count.saturating_add(line_count); - - // Make `DrawTarget` forget about the zombie lines so that they aren't cleared on next draw. - self.draw_target - .adjust_last_line_count(LineAdjust::Keep(line_count)); - - self.remove_idx(index); - } - - pub(crate) fn draw( - &mut self, - mut force_draw: bool, - extra_lines: Option<Vec<String>>, - now: Instant, - ) -> io::Result<()> { - if panicking() { - return Ok(()); - } - let width = self.width() as f64; - // Calculate real length based on terminal width - // This take in account linewrap from terminal - fn real_len(lines: &[String], width: f64) -> usize { - lines.iter().fold(0, |sum, val| { - sum + (console::measure_text_width(val) as f64 / width).ceil() as usize - }) - } - - // Assumption: if extra_lines is not None, then it has at least one line - debug_assert_eq!( - extra_lines.is_some(), - extra_lines.as_ref().map(Vec::len).unwrap_or_default() > 0 - ); - - let mut reap_indices = vec![]; - - // Reap all consecutive 'zombie' progress bars from head of the list. - let mut adjust = 0; - for &index in &self.ordering { - let member = &self.members[index]; - if !member.is_zombie { - break; - } - - let line_count = member - .draw_state - .as_ref() - .map(|d| real_len(&d.lines, width)) - .unwrap_or_default(); - // Track the total number of zombie lines on the screen. - self.zombie_lines_count += line_count; - - // Track the number of zombie lines that will be drawn by this call to draw. - adjust += line_count; - - reap_indices.push(index); - } - - // If this draw is due to a `println`, then we need to erase all the zombie lines. - // This is because `println` is supposed to appear above all other elements in the - // `MultiProgress`. - if extra_lines.is_some() { - self.draw_target - .adjust_last_line_count(LineAdjust::Clear(self.zombie_lines_count)); - self.zombie_lines_count = 0; - } - - let orphan_lines_count = real_len(&self.orphan_lines, width); - force_draw |= orphan_lines_count > 0; - let mut drawable = match self.draw_target.drawable(force_draw, now) { - Some(drawable) => drawable, - None => return Ok(()), - }; - - let mut draw_state = drawable.state(); - draw_state.orphan_lines_count = orphan_lines_count; - draw_state.alignment = self.alignment; - - if let Some(extra_lines) = &extra_lines { - draw_state.lines.extend_from_slice(extra_lines.as_slice()); - draw_state.orphan_lines_count += real_len(extra_lines, width); - } - - // Add lines from `ProgressBar::println` call. - draw_state.lines.append(&mut self.orphan_lines); - - for index in &self.ordering { - let member = &self.members[*index]; - if let Some(state) = &member.draw_state { - draw_state.lines.extend_from_slice(&state.lines[..]); - } - } - - drop(draw_state); - let drawable = drawable.draw(); - - for index in reap_indices { - self.remove_idx(index); - } - - // The zombie lines were drawn for the last time, so make `DrawTarget` forget about them - // so they aren't cleared on next draw. - if extra_lines.is_none() { - self.draw_target - .adjust_last_line_count(LineAdjust::Keep(adjust)); - } - - drawable - } - - pub(crate) fn println<I: AsRef<str>>(&mut self, msg: I, now: Instant) -> io::Result<()> { - let msg = msg.as_ref(); - - // If msg is "", make sure a line is still printed - let lines: Vec<String> = match msg.is_empty() { - false => msg.lines().map(Into::into).collect(), - true => vec![String::new()], - }; - - self.draw(true, Some(lines), now) - } - - pub(crate) fn draw_state(&mut self, idx: usize) -> DrawStateWrapper<'_> { - let member = self.members.get_mut(idx).unwrap(); - // alignment is handled by the `MultiProgress`'s underlying draw target, so there is no - // point in propagating it here. - let state = member.draw_state.get_or_insert(DrawState { - move_cursor: self.move_cursor, - ..Default::default() - }); - - DrawStateWrapper::for_multi(state, &mut self.orphan_lines) - } - - pub(crate) fn is_hidden(&self) -> bool { - self.draw_target.is_hidden() - } - - pub(crate) fn suspend<F: FnOnce() -> R, R>(&mut self, f: F, now: Instant) -> R { - self.clear(now).unwrap(); - let ret = f(); - self.draw(true, None, Instant::now()).unwrap(); - ret - } - - pub(crate) fn width(&self) -> u16 { - self.draw_target.width() - } - - fn insert(&mut self, location: InsertLocation) -> usize { - let idx = if let Some(idx) = self.free_set.pop() { - self.members[idx] = MultiStateMember::default(); - idx - } else { - self.members.push(MultiStateMember::default()); - self.members.len() - 1 - }; - - match location { - InsertLocation::End => self.ordering.push(idx), - InsertLocation::Index(pos) => { - let pos = Ord::min(pos, self.ordering.len()); - self.ordering.insert(pos, idx); - } - InsertLocation::IndexFromBack(pos) => { - let pos = self.ordering.len().saturating_sub(pos); - self.ordering.insert(pos, idx); - } - InsertLocation::After(after_idx) => { - let pos = self.ordering.iter().position(|i| *i == after_idx).unwrap(); - self.ordering.insert(pos + 1, idx); - } - InsertLocation::Before(before_idx) => { - let pos = self.ordering.iter().position(|i| *i == before_idx).unwrap(); - self.ordering.insert(pos, idx); - } - } - - assert_eq!( - self.len(), - self.ordering.len(), - "Draw state is inconsistent" - ); - - idx - } - - fn clear(&mut self, now: Instant) -> io::Result<()> { - match self.draw_target.drawable(true, now) { - Some(mut drawable) => { - // Make the clear operation also wipe out zombie lines - drawable.adjust_last_line_count(LineAdjust::Clear(self.zombie_lines_count)); - self.zombie_lines_count = 0; - drawable.clear() - } - None => Ok(()), - } - } - - fn remove_idx(&mut self, idx: usize) { - if self.free_set.contains(&idx) { - return; - } - - self.members[idx] = MultiStateMember::default(); - self.free_set.push(idx); - self.ordering.retain(|&x| x != idx); - - assert_eq!( - self.len(), - self.ordering.len(), - "Draw state is inconsistent" - ); - } - - fn len(&self) -> usize { - self.members.len() - self.free_set.len() - } -} - -#[derive(Default)] -struct MultiStateMember { - /// Draw state will be `None` for members that haven't been drawn before, or for entries that - /// correspond to something in the free set. - draw_state: Option<DrawState>, - /// Whether the corresponding progress bar (more precisely, `BarState`) has been dropped. - is_zombie: bool, -} - -impl Debug for MultiStateMember { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("MultiStateElement") - .field("draw_state", &self.draw_state) - .field("is_zombie", &self.is_zombie) - .finish_non_exhaustive() - } -} - -/// Vertical alignment of a multi progress. -/// -/// The alignment controls how the multi progress is aligned if some of its progress bars get removed. -/// E.g. `Top` alignment (default), when _progress bar 2_ is removed: -/// ```ignore -/// [0/100] progress bar 1 [0/100] progress bar 1 -/// [0/100] progress bar 2 => [0/100] progress bar 3 -/// [0/100] progress bar 3 -/// ``` -/// -/// `Bottom` alignment -/// ```ignore -/// [0/100] progress bar 1 -/// [0/100] progress bar 2 => [0/100] progress bar 1 -/// [0/100] progress bar 3 [0/100] progress bar 3 -/// ``` -#[derive(Debug, Copy, Clone)] -pub enum MultiProgressAlignment { - Top, - Bottom, -} - -impl Default for MultiProgressAlignment { - fn default() -> Self { - Self::Top - } -} - -enum InsertLocation { - End, - Index(usize), - IndexFromBack(usize), - After(usize), - Before(usize), -} - -#[cfg(test)] -mod tests { - use crate::{MultiProgress, ProgressBar, ProgressDrawTarget}; - - #[test] - fn late_pb_drop() { - let pb = ProgressBar::new(10); - let mpb = MultiProgress::new(); - // This clone call is required to trigger a now fixed bug. - // See <https://github.com/console-rs/indicatif/pull/141> for context - #[allow(clippy::redundant_clone)] - mpb.add(pb.clone()); - } - - #[test] - fn progress_bar_sync_send() { - let _: Box<dyn Sync> = Box::new(ProgressBar::new(1)); - let _: Box<dyn Send> = Box::new(ProgressBar::new(1)); - let _: Box<dyn Sync> = Box::new(MultiProgress::new()); - let _: Box<dyn Send> = Box::new(MultiProgress::new()); - } - - #[test] - fn multi_progress_hidden() { - let mpb = MultiProgress::with_draw_target(ProgressDrawTarget::hidden()); - let pb = mpb.add(ProgressBar::new(123)); - pb.finish(); - } - - #[test] - fn multi_progress_modifications() { - let mp = MultiProgress::new(); - let p0 = mp.add(ProgressBar::new(1)); - let p1 = mp.add(ProgressBar::new(1)); - let p2 = mp.add(ProgressBar::new(1)); - let p3 = mp.add(ProgressBar::new(1)); - mp.remove(&p2); - mp.remove(&p1); - let p4 = mp.insert(1, ProgressBar::new(1)); - - let state = mp.state.read().unwrap(); - // the removed place for p1 is reused - assert_eq!(state.members.len(), 4); - assert_eq!(state.len(), 3); - - // free_set may contain 1 or 2 - match state.free_set.last() { - Some(1) => { - assert_eq!(state.ordering, vec![0, 2, 3]); - assert!(state.members[1].draw_state.is_none()); - assert_eq!(p4.index().unwrap(), 2); - } - Some(2) => { - assert_eq!(state.ordering, vec![0, 1, 3]); - assert!(state.members[2].draw_state.is_none()); - assert_eq!(p4.index().unwrap(), 1); - } - _ => unreachable!(), - } - - assert_eq!(p0.index().unwrap(), 0); - assert_eq!(p1.index(), None); - assert_eq!(p2.index(), None); - assert_eq!(p3.index().unwrap(), 3); - } - - #[test] - fn multi_progress_insert_from_back() { - let mp = MultiProgress::new(); - let p0 = mp.add(ProgressBar::new(1)); - let p1 = mp.add(ProgressBar::new(1)); - let p2 = mp.add(ProgressBar::new(1)); - let p3 = mp.insert_from_back(1, ProgressBar::new(1)); - let p4 = mp.insert_from_back(10, ProgressBar::new(1)); - - let state = mp.state.read().unwrap(); - assert_eq!(state.ordering, vec![4, 0, 1, 3, 2]); - assert_eq!(p0.index().unwrap(), 0); - assert_eq!(p1.index().unwrap(), 1); - assert_eq!(p2.index().unwrap(), 2); - assert_eq!(p3.index().unwrap(), 3); - assert_eq!(p4.index().unwrap(), 4); - } - - #[test] - fn multi_progress_insert_after() { - let mp = MultiProgress::new(); - let p0 = mp.add(ProgressBar::new(1)); - let p1 = mp.add(ProgressBar::new(1)); - let p2 = mp.add(ProgressBar::new(1)); - let p3 = mp.insert_after(&p2, ProgressBar::new(1)); - let p4 = mp.insert_after(&p0, ProgressBar::new(1)); - - let state = mp.state.read().unwrap(); - assert_eq!(state.ordering, vec![0, 4, 1, 2, 3]); - assert_eq!(p0.index().unwrap(), 0); - assert_eq!(p1.index().unwrap(), 1); - assert_eq!(p2.index().unwrap(), 2); - assert_eq!(p3.index().unwrap(), 3); - assert_eq!(p4.index().unwrap(), 4); - } - - #[test] - fn multi_progress_insert_before() { - let mp = MultiProgress::new(); - let p0 = mp.add(ProgressBar::new(1)); - let p1 = mp.add(ProgressBar::new(1)); - let p2 = mp.add(ProgressBar::new(1)); - let p3 = mp.insert_before(&p0, ProgressBar::new(1)); - let p4 = mp.insert_before(&p2, ProgressBar::new(1)); - - let state = mp.state.read().unwrap(); - assert_eq!(state.ordering, vec![3, 0, 1, 4, 2]); - assert_eq!(p0.index().unwrap(), 0); - assert_eq!(p1.index().unwrap(), 1); - assert_eq!(p2.index().unwrap(), 2); - assert_eq!(p3.index().unwrap(), 3); - assert_eq!(p4.index().unwrap(), 4); - } - - #[test] - fn multi_progress_insert_before_and_after() { - let mp = MultiProgress::new(); - let p0 = mp.add(ProgressBar::new(1)); - let p1 = mp.add(ProgressBar::new(1)); - let p2 = mp.add(ProgressBar::new(1)); - let p3 = mp.insert_before(&p0, ProgressBar::new(1)); - let p4 = mp.insert_after(&p3, ProgressBar::new(1)); - let p5 = mp.insert_after(&p3, ProgressBar::new(1)); - let p6 = mp.insert_before(&p1, ProgressBar::new(1)); - - let state = mp.state.read().unwrap(); - assert_eq!(state.ordering, vec![3, 5, 4, 0, 6, 1, 2]); - assert_eq!(p0.index().unwrap(), 0); - assert_eq!(p1.index().unwrap(), 1); - assert_eq!(p2.index().unwrap(), 2); - assert_eq!(p3.index().unwrap(), 3); - assert_eq!(p4.index().unwrap(), 4); - assert_eq!(p5.index().unwrap(), 5); - assert_eq!(p6.index().unwrap(), 6); - } - - #[test] - fn multi_progress_multiple_remove() { - let mp = MultiProgress::new(); - let p0 = mp.add(ProgressBar::new(1)); - let p1 = mp.add(ProgressBar::new(1)); - // double remove beyond the first one have no effect - mp.remove(&p0); - mp.remove(&p0); - mp.remove(&p0); - - let state = mp.state.read().unwrap(); - // the removed place for p1 is reused - assert_eq!(state.members.len(), 2); - assert_eq!(state.free_set.len(), 1); - assert_eq!(state.len(), 1); - assert!(state.members[0].draw_state.is_none()); - assert_eq!(state.free_set.last(), Some(&0)); - - assert_eq!(state.ordering, vec![1]); - assert_eq!(p0.index(), None); - assert_eq!(p1.index().unwrap(), 1); - } - - #[test] - fn mp_no_crash_double_add() { - let mp = MultiProgress::new(); - let pb = mp.add(ProgressBar::new(10)); - mp.add(pb); - } -} diff --git a/vendor/indicatif/src/progress_bar.rs b/vendor/indicatif/src/progress_bar.rs deleted file mode 100644 index 938668e..0000000 --- a/vendor/indicatif/src/progress_bar.rs +++ /dev/null @@ -1,808 +0,0 @@ -#[cfg(test)] -use portable_atomic::{AtomicBool, Ordering}; -use std::borrow::Cow; -use std::sync::{Arc, Condvar, Mutex, MutexGuard, Weak}; -use std::time::Duration; -#[cfg(not(target_arch = "wasm32"))] -use std::time::Instant; -use std::{fmt, io, thread}; - -#[cfg(target_arch = "wasm32")] -use instant::Instant; -#[cfg(test)] -use once_cell::sync::Lazy; - -use crate::draw_target::ProgressDrawTarget; -use crate::state::{AtomicPosition, BarState, ProgressFinish, Reset, TabExpandedString}; -use crate::style::ProgressStyle; -use crate::{ProgressBarIter, ProgressIterator, ProgressState}; - -/// A progress bar or spinner -/// -/// The progress bar is an [`Arc`] around its internal state. When the progress bar is cloned it -/// just increments the refcount (so the original and its clone share the same state). -#[derive(Clone)] -pub struct ProgressBar { - state: Arc<Mutex<BarState>>, - pos: Arc<AtomicPosition>, - ticker: Arc<Mutex<Option<Ticker>>>, -} - -impl fmt::Debug for ProgressBar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ProgressBar").finish() - } -} - -impl ProgressBar { - /// Creates a new progress bar with a given length - /// - /// This progress bar by default draws directly to stderr, and refreshes a maximum of 15 times - /// a second. To change the refresh rate, set the draw target to one with a different refresh - /// rate. - pub fn new(len: u64) -> Self { - Self::with_draw_target(Some(len), ProgressDrawTarget::stderr()) - } - - /// Creates a completely hidden progress bar - /// - /// This progress bar still responds to API changes but it does not have a length or render in - /// any way. - pub fn hidden() -> Self { - Self::with_draw_target(None, ProgressDrawTarget::hidden()) - } - - /// Creates a new progress bar with a given length and draw target - pub fn with_draw_target(len: Option<u64>, draw_target: ProgressDrawTarget) -> Self { - let pos = Arc::new(AtomicPosition::new()); - Self { - state: Arc::new(Mutex::new(BarState::new(len, draw_target, pos.clone()))), - pos, - ticker: Arc::new(Mutex::new(None)), - } - } - - /// Get a clone of the current progress bar style. - pub fn style(&self) -> ProgressStyle { - self.state().style.clone() - } - - /// A convenience builder-like function for a progress bar with a given style - pub fn with_style(self, style: ProgressStyle) -> Self { - self.set_style(style); - self - } - - /// A convenience builder-like function for a progress bar with a given tab width - pub fn with_tab_width(self, tab_width: usize) -> Self { - self.state().set_tab_width(tab_width); - self - } - - /// A convenience builder-like function for a progress bar with a given prefix - /// - /// For the prefix to be visible, the `{prefix}` placeholder must be present in the template - /// (see [`ProgressStyle`]). - pub fn with_prefix(self, prefix: impl Into<Cow<'static, str>>) -> Self { - let mut state = self.state(); - state.state.prefix = TabExpandedString::new(prefix.into(), state.tab_width); - drop(state); - self - } - - /// A convenience builder-like function for a progress bar with a given message - /// - /// For the message to be visible, the `{msg}` placeholder must be present in the template (see - /// [`ProgressStyle`]). - pub fn with_message(self, message: impl Into<Cow<'static, str>>) -> Self { - let mut state = self.state(); - state.state.message = TabExpandedString::new(message.into(), state.tab_width); - drop(state); - self - } - - /// A convenience builder-like function for a progress bar with a given position - pub fn with_position(self, pos: u64) -> Self { - self.state().state.set_pos(pos); - self - } - - /// A convenience builder-like function for a progress bar with a given elapsed time - pub fn with_elapsed(self, elapsed: Duration) -> Self { - self.state().state.started = Instant::now().checked_sub(elapsed).unwrap(); - self - } - - /// Sets the finish behavior for the progress bar - /// - /// This behavior is invoked when [`ProgressBar`] or - /// [`ProgressBarIter`] completes and - /// [`ProgressBar::is_finished()`] is false. - /// If you don't want the progress bar to be automatically finished then - /// call `on_finish(None)`. - /// - /// [`ProgressBar`]: crate::ProgressBar - /// [`ProgressBarIter`]: crate::ProgressBarIter - /// [`ProgressBar::is_finished()`]: crate::ProgressBar::is_finished - pub fn with_finish(self, finish: ProgressFinish) -> Self { - self.state().on_finish = finish; - self - } - - /// Creates a new spinner - /// - /// This spinner by default draws directly to stderr. This adds the default spinner style to it. - pub fn new_spinner() -> Self { - let rv = Self::with_draw_target(None, ProgressDrawTarget::stderr()); - rv.set_style(ProgressStyle::default_spinner()); - rv - } - - /// Overrides the stored style - /// - /// This does not redraw the bar. Call [`ProgressBar::tick()`] to force it. - pub fn set_style(&self, style: ProgressStyle) { - self.state().set_style(style); - } - - /// Sets the tab width (default: 8). All tabs will be expanded to this many spaces. - pub fn set_tab_width(&mut self, tab_width: usize) { - let mut state = self.state(); - state.set_tab_width(tab_width); - state.draw(true, Instant::now()).unwrap(); - } - - /// Spawns a background thread to tick the progress bar - /// - /// When this is enabled a background thread will regularly tick the progress bar in the given - /// interval. This is useful to advance progress bars that are very slow by themselves. - /// - /// When steady ticks are enabled, calling [`ProgressBar::tick()`] on a progress bar does not - /// have any effect. - pub fn enable_steady_tick(&self, interval: Duration) { - // The way we test for ticker termination is with a single static `AtomicBool`. Since cargo - // runs tests concurrently, we have a `TICKER_TEST` lock to make sure tests using ticker - // don't step on each other. This check catches attempts to use tickers in tests without - // acquiring the lock. - #[cfg(test)] - { - let guard = TICKER_TEST.try_lock(); - let lock_acquired = guard.is_ok(); - // Drop the guard before panicking to avoid poisoning the lock (which would cause other - // ticker tests to fail) - drop(guard); - if lock_acquired { - panic!("you must acquire the TICKER_TEST lock in your test to use this method"); - } - } - - if interval.is_zero() { - return; - } - - self.stop_and_replace_ticker(Some(interval)); - } - - /// Undoes [`ProgressBar::enable_steady_tick()`] - pub fn disable_steady_tick(&self) { - self.stop_and_replace_ticker(None); - } - - fn stop_and_replace_ticker(&self, interval: Option<Duration>) { - let mut ticker_state = self.ticker.lock().unwrap(); - if let Some(ticker) = ticker_state.take() { - ticker.stop(); - } - - *ticker_state = interval.map(|interval| Ticker::new(interval, &self.state)); - } - - /// Manually ticks the spinner or progress bar - /// - /// This automatically happens on any other change to a progress bar. - pub fn tick(&self) { - self.tick_inner(Instant::now()); - } - - fn tick_inner(&self, now: Instant) { - // Only tick if a `Ticker` isn't installed - if self.ticker.lock().unwrap().is_none() { - self.state().tick(now); - } - } - - /// Advances the position of the progress bar by `delta` - pub fn inc(&self, delta: u64) { - self.pos.inc(delta); - let now = Instant::now(); - if self.pos.allow(now) { - self.tick_inner(now); - } - } - - /// A quick convenience check if the progress bar is hidden - pub fn is_hidden(&self) -> bool { - self.state().draw_target.is_hidden() - } - - /// Indicates that the progress bar finished - pub fn is_finished(&self) -> bool { - self.state().state.is_finished() - } - - /// Print a log line above the progress bar - /// - /// If the progress bar is hidden (e.g. when standard output is not a terminal), `println()` - /// will not do anything. If you want to write to the standard output in such cases as well, use - /// [`suspend`] instead. - /// - /// If the progress bar was added to a [`MultiProgress`], the log line will be - /// printed above all other progress bars. - /// - /// [`suspend`]: ProgressBar::suspend - /// [`MultiProgress`]: crate::MultiProgress - pub fn println<I: AsRef<str>>(&self, msg: I) { - self.state().println(Instant::now(), msg.as_ref()); - } - - /// Update the `ProgressBar`'s inner [`ProgressState`] - pub fn update(&self, f: impl FnOnce(&mut ProgressState)) { - self.state() - .update(Instant::now(), f, self.ticker.lock().unwrap().is_none()); - } - - /// Sets the position of the progress bar - pub fn set_position(&self, pos: u64) { - self.pos.set(pos); - let now = Instant::now(); - if self.pos.allow(now) { - self.tick_inner(now); - } - } - - /// Sets the length of the progress bar - pub fn set_length(&self, len: u64) { - self.state().set_length(Instant::now(), len); - } - - /// Increase the length of the progress bar - pub fn inc_length(&self, delta: u64) { - self.state().inc_length(Instant::now(), delta); - } - - /// Sets the current prefix of the progress bar - /// - /// For the prefix to be visible, the `{prefix}` placeholder must be present in the template - /// (see [`ProgressStyle`]). - pub fn set_prefix(&self, prefix: impl Into<Cow<'static, str>>) { - let mut state = self.state(); - state.state.prefix = TabExpandedString::new(prefix.into(), state.tab_width); - state.update_estimate_and_draw(Instant::now()); - } - - /// Sets the current message of the progress bar - /// - /// For the message to be visible, the `{msg}` placeholder must be present in the template (see - /// [`ProgressStyle`]). - pub fn set_message(&self, msg: impl Into<Cow<'static, str>>) { - let mut state = self.state(); - state.state.message = TabExpandedString::new(msg.into(), state.tab_width); - state.update_estimate_and_draw(Instant::now()); - } - - /// Creates a new weak reference to this `ProgressBar` - pub fn downgrade(&self) -> WeakProgressBar { - WeakProgressBar { - state: Arc::downgrade(&self.state), - pos: Arc::downgrade(&self.pos), - ticker: Arc::downgrade(&self.ticker), - } - } - - /// Resets the ETA calculation - /// - /// This can be useful if the progress bars made a large jump or was paused for a prolonged - /// time. - pub fn reset_eta(&self) { - self.state().reset(Instant::now(), Reset::Eta); - } - - /// Resets elapsed time and the ETA calculation - pub fn reset_elapsed(&self) { - self.state().reset(Instant::now(), Reset::Elapsed); - } - - /// Resets all of the progress bar state - pub fn reset(&self) { - self.state().reset(Instant::now(), Reset::All); - } - - /// Finishes the progress bar and leaves the current message - pub fn finish(&self) { - self.state() - .finish_using_style(Instant::now(), ProgressFinish::AndLeave); - } - - /// Finishes the progress bar and sets a message - /// - /// For the message to be visible, the `{msg}` placeholder must be present in the template (see - /// [`ProgressStyle`]). - pub fn finish_with_message(&self, msg: impl Into<Cow<'static, str>>) { - self.state() - .finish_using_style(Instant::now(), ProgressFinish::WithMessage(msg.into())); - } - - /// Finishes the progress bar and completely clears it - pub fn finish_and_clear(&self) { - self.state() - .finish_using_style(Instant::now(), ProgressFinish::AndClear); - } - - /// Finishes the progress bar and leaves the current message and progress - pub fn abandon(&self) { - self.state() - .finish_using_style(Instant::now(), ProgressFinish::Abandon); - } - - /// Finishes the progress bar and sets a message, and leaves the current progress - /// - /// For the message to be visible, the `{msg}` placeholder must be present in the template (see - /// [`ProgressStyle`]). - pub fn abandon_with_message(&self, msg: impl Into<Cow<'static, str>>) { - self.state().finish_using_style( - Instant::now(), - ProgressFinish::AbandonWithMessage(msg.into()), - ); - } - - /// Finishes the progress bar using the behavior stored in the [`ProgressStyle`] - /// - /// See [`ProgressBar::with_finish()`]. - pub fn finish_using_style(&self) { - let mut state = self.state(); - let finish = state.on_finish.clone(); - state.finish_using_style(Instant::now(), finish); - } - - /// Sets a different draw target for the progress bar - /// - /// This can be used to draw the progress bar to stderr (this is the default): - /// - /// ```rust,no_run - /// # use indicatif::{ProgressBar, ProgressDrawTarget}; - /// let pb = ProgressBar::new(100); - /// pb.set_draw_target(ProgressDrawTarget::stderr()); - /// ``` - /// - /// **Note:** Calling this method on a [`ProgressBar`] linked with a [`MultiProgress`] (after - /// running [`MultiProgress::add`]) will unlink this progress bar. If you don't want this - /// behavior, call [`MultiProgress::set_draw_target`] instead. - /// - /// [`MultiProgress`]: crate::MultiProgress - /// [`MultiProgress::add`]: crate::MultiProgress::add - /// [`MultiProgress::set_draw_target`]: crate::MultiProgress::set_draw_target - pub fn set_draw_target(&self, target: ProgressDrawTarget) { - let mut state = self.state(); - state.draw_target.disconnect(Instant::now()); - state.draw_target = target; - } - - /// Hide the progress bar temporarily, execute `f`, then redraw the progress bar - /// - /// Useful for external code that writes to the standard output. - /// - /// If the progress bar was added to a MultiProgress, it will suspend the entire MultiProgress - /// - /// **Note:** The internal lock is held while `f` is executed. Other threads trying to print - /// anything on the progress bar will be blocked until `f` finishes. - /// Therefore, it is recommended to avoid long-running operations in `f`. - /// - /// ```rust,no_run - /// # use indicatif::ProgressBar; - /// let mut pb = ProgressBar::new(3); - /// pb.suspend(|| { - /// println!("Log message"); - /// }) - /// ``` - pub fn suspend<F: FnOnce() -> R, R>(&self, f: F) -> R { - self.state().suspend(Instant::now(), f) - } - - /// Wraps an [`Iterator`] with the progress bar - /// - /// ```rust,no_run - /// # use indicatif::ProgressBar; - /// let v = vec![1, 2, 3]; - /// let pb = ProgressBar::new(3); - /// for item in pb.wrap_iter(v.iter()) { - /// // ... - /// } - /// ``` - pub fn wrap_iter<It: Iterator>(&self, it: It) -> ProgressBarIter<It> { - it.progress_with(self.clone()) - } - - /// Wraps an [`io::Read`] with the progress bar - /// - /// ```rust,no_run - /// # use std::fs::File; - /// # use std::io; - /// # use indicatif::ProgressBar; - /// # fn test () -> io::Result<()> { - /// let source = File::open("work.txt")?; - /// let mut target = File::create("done.txt")?; - /// let pb = ProgressBar::new(source.metadata()?.len()); - /// io::copy(&mut pb.wrap_read(source), &mut target); - /// # Ok(()) - /// # } - /// ``` - pub fn wrap_read<R: io::Read>(&self, read: R) -> ProgressBarIter<R> { - ProgressBarIter { - progress: self.clone(), - it: read, - } - } - - /// Wraps an [`io::Write`] with the progress bar - /// - /// ```rust,no_run - /// # use std::fs::File; - /// # use std::io; - /// # use indicatif::ProgressBar; - /// # fn test () -> io::Result<()> { - /// let mut source = File::open("work.txt")?; - /// let target = File::create("done.txt")?; - /// let pb = ProgressBar::new(source.metadata()?.len()); - /// io::copy(&mut source, &mut pb.wrap_write(target)); - /// # Ok(()) - /// # } - /// ``` - pub fn wrap_write<W: io::Write>(&self, write: W) -> ProgressBarIter<W> { - ProgressBarIter { - progress: self.clone(), - it: write, - } - } - - #[cfg(feature = "tokio")] - #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] - /// Wraps an [`tokio::io::AsyncWrite`] with the progress bar - /// - /// ```rust,no_run - /// # use tokio::fs::File; - /// # use tokio::io; - /// # use indicatif::ProgressBar; - /// # async fn test() -> io::Result<()> { - /// let mut source = File::open("work.txt").await?; - /// let mut target = File::open("done.txt").await?; - /// let pb = ProgressBar::new(source.metadata().await?.len()); - /// io::copy(&mut source, &mut pb.wrap_async_write(target)).await?; - /// # Ok(()) - /// # } - /// ``` - pub fn wrap_async_write<W: tokio::io::AsyncWrite + Unpin>( - &self, - write: W, - ) -> ProgressBarIter<W> { - ProgressBarIter { - progress: self.clone(), - it: write, - } - } - - #[cfg(feature = "tokio")] - #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] - /// Wraps an [`tokio::io::AsyncRead`] with the progress bar - /// - /// ```rust,no_run - /// # use tokio::fs::File; - /// # use tokio::io; - /// # use indicatif::ProgressBar; - /// # async fn test() -> io::Result<()> { - /// let mut source = File::open("work.txt").await?; - /// let mut target = File::open("done.txt").await?; - /// let pb = ProgressBar::new(source.metadata().await?.len()); - /// io::copy(&mut pb.wrap_async_read(source), &mut target).await?; - /// # Ok(()) - /// # } - /// ``` - pub fn wrap_async_read<R: tokio::io::AsyncRead + Unpin>(&self, read: R) -> ProgressBarIter<R> { - ProgressBarIter { - progress: self.clone(), - it: read, - } - } - - /// Wraps a [`futures::Stream`](https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html) with the progress bar - /// - /// ``` - /// # use indicatif::ProgressBar; - /// # futures::executor::block_on(async { - /// use futures::stream::{self, StreamExt}; - /// let pb = ProgressBar::new(10); - /// let mut stream = pb.wrap_stream(stream::iter('a'..='z')); - /// - /// assert_eq!(stream.next().await, Some('a')); - /// assert_eq!(stream.count().await, 25); - /// # }); // block_on - /// ``` - #[cfg(feature = "futures")] - #[cfg_attr(docsrs, doc(cfg(feature = "futures")))] - pub fn wrap_stream<S: futures_core::Stream>(&self, stream: S) -> ProgressBarIter<S> { - ProgressBarIter { - progress: self.clone(), - it: stream, - } - } - - /// Returns the current position - pub fn position(&self) -> u64 { - self.state().state.pos() - } - - /// Returns the current length - pub fn length(&self) -> Option<u64> { - self.state().state.len() - } - - /// Returns the current ETA - pub fn eta(&self) -> Duration { - self.state().state.eta() - } - - /// Returns the current rate of progress - pub fn per_sec(&self) -> f64 { - self.state().state.per_sec() - } - - /// Returns the current expected duration - pub fn duration(&self) -> Duration { - self.state().state.duration() - } - - /// Returns the current elapsed time - pub fn elapsed(&self) -> Duration { - self.state().state.elapsed() - } - - /// Index in the `MultiState` - pub(crate) fn index(&self) -> Option<usize> { - self.state().draw_target.remote().map(|(_, idx)| idx) - } - - /// Current message - pub fn message(&self) -> String { - self.state().state.message.expanded().to_string() - } - - /// Current prefix - pub fn prefix(&self) -> String { - self.state().state.prefix.expanded().to_string() - } - - #[inline] - pub(crate) fn state(&self) -> MutexGuard<'_, BarState> { - self.state.lock().unwrap() - } -} - -/// A weak reference to a `ProgressBar`. -/// -/// Useful for creating custom steady tick implementations -#[derive(Clone, Default)] -pub struct WeakProgressBar { - state: Weak<Mutex<BarState>>, - pos: Weak<AtomicPosition>, - ticker: Weak<Mutex<Option<Ticker>>>, -} - -impl WeakProgressBar { - /// Create a new `WeakProgressBar` that returns `None` when [`upgrade`] is called. - /// - /// [`upgrade`]: WeakProgressBar::upgrade - pub fn new() -> Self { - Self::default() - } - - /// Attempts to upgrade the Weak pointer to a [`ProgressBar`], delaying dropping of the inner - /// value if successful. Returns `None` if the inner value has since been dropped. - /// - /// [`ProgressBar`]: struct.ProgressBar.html - pub fn upgrade(&self) -> Option<ProgressBar> { - let state = self.state.upgrade()?; - let pos = self.pos.upgrade()?; - let ticker = self.ticker.upgrade()?; - Some(ProgressBar { state, pos, ticker }) - } -} - -pub(crate) struct Ticker { - stopping: Arc<(Mutex<bool>, Condvar)>, - join_handle: Option<thread::JoinHandle<()>>, -} - -impl Drop for Ticker { - fn drop(&mut self) { - self.stop(); - self.join_handle.take().map(|handle| handle.join()); - } -} - -#[cfg(test)] -static TICKER_RUNNING: AtomicBool = AtomicBool::new(false); - -impl Ticker { - pub(crate) fn new(interval: Duration, bar_state: &Arc<Mutex<BarState>>) -> Self { - debug_assert!(!interval.is_zero()); - - // A `Mutex<bool>` is used as a flag to indicate whether the ticker was requested to stop. - // The `Condvar` is used a notification mechanism: when the ticker is dropped, we notify - // the thread and interrupt the ticker wait. - #[allow(clippy::mutex_atomic)] - let stopping = Arc::new((Mutex::new(false), Condvar::new())); - let control = TickerControl { - stopping: stopping.clone(), - state: Arc::downgrade(bar_state), - }; - - let join_handle = thread::spawn(move || control.run(interval)); - Self { - stopping, - join_handle: Some(join_handle), - } - } - - pub(crate) fn stop(&self) { - *self.stopping.0.lock().unwrap() = true; - self.stopping.1.notify_one(); - } -} - -struct TickerControl { - stopping: Arc<(Mutex<bool>, Condvar)>, - state: Weak<Mutex<BarState>>, -} - -impl TickerControl { - fn run(&self, interval: Duration) { - #[cfg(test)] - TICKER_RUNNING.store(true, Ordering::SeqCst); - - while let Some(arc) = self.state.upgrade() { - let mut state = arc.lock().unwrap(); - if state.state.is_finished() { - break; - } - - state.tick(Instant::now()); - - drop(state); // Don't forget to drop the lock before sleeping - drop(arc); // Also need to drop Arc otherwise BarState won't be dropped - - // Wait for `interval` but return early if we are notified to stop - let (_, result) = self - .stopping - .1 - .wait_timeout_while(self.stopping.0.lock().unwrap(), interval, |stopped| { - !*stopped - }) - .unwrap(); - - // If the wait didn't time out, it means we were notified to stop - if !result.timed_out() { - break; - } - } - - #[cfg(test)] - TICKER_RUNNING.store(false, Ordering::SeqCst); - } -} - -// Tests using the global TICKER_RUNNING flag need to be serialized -#[cfg(test)] -pub(crate) static TICKER_TEST: Lazy<Mutex<()>> = Lazy::new(Mutex::default); - -#[cfg(test)] -mod tests { - use super::*; - - #[allow(clippy::float_cmp)] - #[test] - fn test_pbar_zero() { - let pb = ProgressBar::new(0); - assert_eq!(pb.state().state.fraction(), 1.0); - } - - #[allow(clippy::float_cmp)] - #[test] - fn test_pbar_maxu64() { - let pb = ProgressBar::new(!0); - assert_eq!(pb.state().state.fraction(), 0.0); - } - - #[test] - fn test_pbar_overflow() { - let pb = ProgressBar::new(1); - pb.set_draw_target(ProgressDrawTarget::hidden()); - pb.inc(2); - pb.finish(); - } - - #[test] - fn test_get_position() { - let pb = ProgressBar::new(1); - pb.set_draw_target(ProgressDrawTarget::hidden()); - pb.inc(2); - let pos = pb.position(); - assert_eq!(pos, 2); - } - - #[test] - fn test_weak_pb() { - let pb = ProgressBar::new(0); - let weak = pb.downgrade(); - assert!(weak.upgrade().is_some()); - ::std::mem::drop(pb); - assert!(weak.upgrade().is_none()); - } - - #[test] - fn it_can_wrap_a_reader() { - let bytes = &b"I am an implementation of io::Read"[..]; - let pb = ProgressBar::new(bytes.len() as u64); - let mut reader = pb.wrap_read(bytes); - let mut writer = Vec::new(); - io::copy(&mut reader, &mut writer).unwrap(); - assert_eq!(writer, bytes); - } - - #[test] - fn it_can_wrap_a_writer() { - let bytes = b"implementation of io::Read"; - let mut reader = &bytes[..]; - let pb = ProgressBar::new(bytes.len() as u64); - let writer = Vec::new(); - let mut writer = pb.wrap_write(writer); - io::copy(&mut reader, &mut writer).unwrap(); - assert_eq!(writer.it, bytes); - } - - #[test] - fn ticker_thread_terminates_on_drop() { - let _guard = TICKER_TEST.lock().unwrap(); - assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); - - let pb = ProgressBar::new_spinner(); - pb.enable_steady_tick(Duration::from_millis(50)); - - // Give the thread time to start up - thread::sleep(Duration::from_millis(250)); - - assert!(TICKER_RUNNING.load(Ordering::SeqCst)); - - drop(pb); - assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); - } - - #[test] - fn ticker_thread_terminates_on_drop_2() { - let _guard = TICKER_TEST.lock().unwrap(); - assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); - - let pb = ProgressBar::new_spinner(); - pb.enable_steady_tick(Duration::from_millis(50)); - let pb2 = pb.clone(); - - // Give the thread time to start up - thread::sleep(Duration::from_millis(250)); - - assert!(TICKER_RUNNING.load(Ordering::SeqCst)); - - drop(pb); - assert!(TICKER_RUNNING.load(Ordering::SeqCst)); - - drop(pb2); - assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); - } -} diff --git a/vendor/indicatif/src/rayon.rs b/vendor/indicatif/src/rayon.rs deleted file mode 100644 index 1c8e844..0000000 --- a/vendor/indicatif/src/rayon.rs +++ /dev/null @@ -1,235 +0,0 @@ -use rayon::iter::plumbing::{Consumer, Folder, Producer, ProducerCallback, UnindexedConsumer}; -use rayon::iter::{IndexedParallelIterator, ParallelIterator}; - -use crate::{ProgressBar, ProgressBarIter}; - -/// Wraps a Rayon parallel iterator. -/// -/// See [`ProgressIterator`](trait.ProgressIterator.html) for method -/// documentation. -#[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] -pub trait ParallelProgressIterator -where - Self: Sized + ParallelIterator, -{ - /// Wrap an iterator with a custom progress bar. - fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self>; - - /// Wrap an iterator with an explicit element count. - fn progress_count(self, len: u64) -> ProgressBarIter<Self> { - self.progress_with(ProgressBar::new(len)) - } - - fn progress(self) -> ProgressBarIter<Self> - where - Self: IndexedParallelIterator, - { - let len = u64::try_from(self.len()).unwrap(); - self.progress_count(len) - } - - /// Wrap an iterator with a progress bar and style it. - fn progress_with_style(self, style: crate::ProgressStyle) -> ProgressBarIter<Self> - where - Self: IndexedParallelIterator, - { - let len = u64::try_from(self.len()).unwrap(); - let bar = ProgressBar::new(len).with_style(style); - self.progress_with(bar) - } -} - -impl<S: Send, T: ParallelIterator<Item = S>> ParallelProgressIterator for T { - fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self> { - ProgressBarIter { it: self, progress } - } -} - -impl<S: Send, T: IndexedParallelIterator<Item = S>> IndexedParallelIterator for ProgressBarIter<T> { - fn len(&self) -> usize { - self.it.len() - } - - fn drive<C: Consumer<Self::Item>>(self, consumer: C) -> <C as Consumer<Self::Item>>::Result { - let consumer = ProgressConsumer::new(consumer, self.progress); - self.it.drive(consumer) - } - - fn with_producer<CB: ProducerCallback<Self::Item>>( - self, - callback: CB, - ) -> <CB as ProducerCallback<Self::Item>>::Output { - return self.it.with_producer(Callback { - callback, - progress: self.progress, - }); - - struct Callback<CB> { - callback: CB, - progress: ProgressBar, - } - - impl<T, CB: ProducerCallback<T>> ProducerCallback<T> for Callback<CB> { - type Output = CB::Output; - - fn callback<P>(self, base: P) -> CB::Output - where - P: Producer<Item = T>, - { - let producer = ProgressProducer { - base, - progress: self.progress, - }; - self.callback.callback(producer) - } - } - } -} - -struct ProgressProducer<T> { - base: T, - progress: ProgressBar, -} - -impl<T, P: Producer<Item = T>> Producer for ProgressProducer<P> { - type Item = T; - type IntoIter = ProgressBarIter<P::IntoIter>; - - fn into_iter(self) -> Self::IntoIter { - ProgressBarIter { - it: self.base.into_iter(), - progress: self.progress, - } - } - - fn min_len(&self) -> usize { - self.base.min_len() - } - - fn max_len(&self) -> usize { - self.base.max_len() - } - - fn split_at(self, index: usize) -> (Self, Self) { - let (left, right) = self.base.split_at(index); - ( - ProgressProducer { - base: left, - progress: self.progress.clone(), - }, - ProgressProducer { - base: right, - progress: self.progress, - }, - ) - } -} - -struct ProgressConsumer<C> { - base: C, - progress: ProgressBar, -} - -impl<C> ProgressConsumer<C> { - fn new(base: C, progress: ProgressBar) -> Self { - ProgressConsumer { base, progress } - } -} - -impl<T, C: Consumer<T>> Consumer<T> for ProgressConsumer<C> { - type Folder = ProgressFolder<C::Folder>; - type Reducer = C::Reducer; - type Result = C::Result; - - fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { - let (left, right, reducer) = self.base.split_at(index); - ( - ProgressConsumer::new(left, self.progress.clone()), - ProgressConsumer::new(right, self.progress), - reducer, - ) - } - - fn into_folder(self) -> Self::Folder { - ProgressFolder { - base: self.base.into_folder(), - progress: self.progress, - } - } - - fn full(&self) -> bool { - self.base.full() - } -} - -impl<T, C: UnindexedConsumer<T>> UnindexedConsumer<T> for ProgressConsumer<C> { - fn split_off_left(&self) -> Self { - ProgressConsumer::new(self.base.split_off_left(), self.progress.clone()) - } - - fn to_reducer(&self) -> Self::Reducer { - self.base.to_reducer() - } -} - -struct ProgressFolder<C> { - base: C, - progress: ProgressBar, -} - -impl<T, C: Folder<T>> Folder<T> for ProgressFolder<C> { - type Result = C::Result; - - fn consume(self, item: T) -> Self { - self.progress.inc(1); - ProgressFolder { - base: self.base.consume(item), - progress: self.progress, - } - } - - fn complete(self) -> C::Result { - self.base.complete() - } - - fn full(&self) -> bool { - self.base.full() - } -} - -impl<S: Send, T: ParallelIterator<Item = S>> ParallelIterator for ProgressBarIter<T> { - type Item = S; - - fn drive_unindexed<C: UnindexedConsumer<Self::Item>>(self, consumer: C) -> C::Result { - let consumer1 = ProgressConsumer::new(consumer, self.progress.clone()); - self.it.drive_unindexed(consumer1) - } -} - -#[cfg(test)] -mod test { - use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; - - use crate::{ParallelProgressIterator, ProgressBar, ProgressBarIter, ProgressStyle}; - - #[test] - fn it_can_wrap_a_parallel_iterator() { - let v = vec![1, 2, 3]; - fn wrap<'a, T: ParallelIterator<Item = &'a i32>>(it: ProgressBarIter<T>) { - assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]); - } - - wrap(v.par_iter().progress_count(3)); - wrap({ - let pb = ProgressBar::new(v.len() as u64); - v.par_iter().progress_with(pb) - }); - - wrap({ - let style = ProgressStyle::default_bar() - .template("{wide_bar:.red} {percent}/100%") - .unwrap(); - v.par_iter().progress_with_style(style) - }); - } -} diff --git a/vendor/indicatif/src/state.rs b/vendor/indicatif/src/state.rs deleted file mode 100644 index 3bcdc44..0000000 --- a/vendor/indicatif/src/state.rs +++ /dev/null @@ -1,798 +0,0 @@ -use std::borrow::Cow; -use std::io; -use std::sync::Arc; -use std::time::Duration; -#[cfg(not(target_arch = "wasm32"))] -use std::time::Instant; - -#[cfg(target_arch = "wasm32")] -use instant::Instant; -use portable_atomic::{AtomicU64, AtomicU8, Ordering}; - -use crate::draw_target::ProgressDrawTarget; -use crate::style::ProgressStyle; - -pub(crate) struct BarState { - pub(crate) draw_target: ProgressDrawTarget, - pub(crate) on_finish: ProgressFinish, - pub(crate) style: ProgressStyle, - pub(crate) state: ProgressState, - pub(crate) tab_width: usize, -} - -impl BarState { - pub(crate) fn new( - len: Option<u64>, - draw_target: ProgressDrawTarget, - pos: Arc<AtomicPosition>, - ) -> Self { - Self { - draw_target, - on_finish: ProgressFinish::default(), - style: ProgressStyle::default_bar(), - state: ProgressState::new(len, pos), - tab_width: DEFAULT_TAB_WIDTH, - } - } - - /// Finishes the progress bar using the [`ProgressFinish`] behavior stored - /// in the [`ProgressStyle`]. - pub(crate) fn finish_using_style(&mut self, now: Instant, finish: ProgressFinish) { - self.state.status = Status::DoneVisible; - match finish { - ProgressFinish::AndLeave => { - if let Some(len) = self.state.len { - self.state.pos.set(len); - } - } - ProgressFinish::WithMessage(msg) => { - if let Some(len) = self.state.len { - self.state.pos.set(len); - } - self.state.message = TabExpandedString::new(msg, self.tab_width); - } - ProgressFinish::AndClear => { - if let Some(len) = self.state.len { - self.state.pos.set(len); - } - self.state.status = Status::DoneHidden; - } - ProgressFinish::Abandon => {} - ProgressFinish::AbandonWithMessage(msg) => { - self.state.message = TabExpandedString::new(msg, self.tab_width); - } - } - - // There's no need to update the estimate here; once the `status` is no longer - // `InProgress`, we will use the length and elapsed time to estimate. - let _ = self.draw(true, now); - } - - pub(crate) fn reset(&mut self, now: Instant, mode: Reset) { - // Always reset the estimator; this is the only reset that will occur if mode is - // `Reset::Eta`. - self.state.est.reset(now); - - if let Reset::Elapsed | Reset::All = mode { - self.state.started = now; - } - - if let Reset::All = mode { - self.state.pos.reset(now); - self.state.status = Status::InProgress; - - for tracker in self.style.format_map.values_mut() { - tracker.reset(&self.state, now); - } - - let _ = self.draw(false, now); - } - } - - pub(crate) fn update(&mut self, now: Instant, f: impl FnOnce(&mut ProgressState), tick: bool) { - f(&mut self.state); - if tick { - self.tick(now); - } - } - - pub(crate) fn set_length(&mut self, now: Instant, len: u64) { - self.state.len = Some(len); - self.update_estimate_and_draw(now); - } - - pub(crate) fn inc_length(&mut self, now: Instant, delta: u64) { - if let Some(len) = self.state.len { - self.state.len = Some(len.saturating_add(delta)); - } - self.update_estimate_and_draw(now); - } - - pub(crate) fn set_tab_width(&mut self, tab_width: usize) { - self.tab_width = tab_width; - self.state.message.set_tab_width(tab_width); - self.state.prefix.set_tab_width(tab_width); - self.style.set_tab_width(tab_width); - } - - pub(crate) fn set_style(&mut self, style: ProgressStyle) { - self.style = style; - self.style.set_tab_width(self.tab_width); - } - - pub(crate) fn tick(&mut self, now: Instant) { - self.state.tick = self.state.tick.saturating_add(1); - self.update_estimate_and_draw(now); - } - - pub(crate) fn update_estimate_and_draw(&mut self, now: Instant) { - let pos = self.state.pos.pos.load(Ordering::Relaxed); - self.state.est.record(pos, now); - - for tracker in self.style.format_map.values_mut() { - tracker.tick(&self.state, now); - } - - let _ = self.draw(false, now); - } - - pub(crate) fn println(&mut self, now: Instant, msg: &str) { - let width = self.draw_target.width(); - let mut drawable = match self.draw_target.drawable(true, now) { - Some(drawable) => drawable, - None => return, - }; - - let mut draw_state = drawable.state(); - let lines: Vec<String> = msg.lines().map(Into::into).collect(); - // Empty msg should trigger newline as we are in println - if lines.is_empty() { - draw_state.lines.push(String::new()); - } else { - draw_state.lines.extend(lines); - } - draw_state.orphan_lines_count = draw_state.lines.len(); - if !matches!(self.state.status, Status::DoneHidden) { - self.style - .format_state(&self.state, &mut draw_state.lines, width); - } - - drop(draw_state); - let _ = drawable.draw(); - } - - pub(crate) fn suspend<F: FnOnce() -> R, R>(&mut self, now: Instant, f: F) -> R { - if let Some((state, _)) = self.draw_target.remote() { - return state.write().unwrap().suspend(f, now); - } - - if let Some(drawable) = self.draw_target.drawable(true, now) { - let _ = drawable.clear(); - } - - let ret = f(); - let _ = self.draw(true, Instant::now()); - ret - } - - pub(crate) fn draw(&mut self, mut force_draw: bool, now: Instant) -> io::Result<()> { - let width = self.draw_target.width(); - - // `|= self.is_finished()` should not be needed here, but we used to always draw for - // finished progress bars, so it's kept as to not cause compatibility issues in weird cases. - force_draw |= self.state.is_finished(); - let mut drawable = match self.draw_target.drawable(force_draw, now) { - Some(drawable) => drawable, - None => return Ok(()), - }; - - let mut draw_state = drawable.state(); - - if !matches!(self.state.status, Status::DoneHidden) { - self.style - .format_state(&self.state, &mut draw_state.lines, width); - } - - drop(draw_state); - drawable.draw() - } -} - -impl Drop for BarState { - fn drop(&mut self) { - // Progress bar is already finished. Do not need to do anything other than notify - // the `MultiProgress` that we're now a zombie. - if self.state.is_finished() { - self.draw_target.mark_zombie(); - return; - } - - self.finish_using_style(Instant::now(), self.on_finish.clone()); - - // Notify the `MultiProgress` that we're now a zombie. - self.draw_target.mark_zombie(); - } -} - -pub(crate) enum Reset { - Eta, - Elapsed, - All, -} - -/// The state of a progress bar at a moment in time. -#[non_exhaustive] -pub struct ProgressState { - pos: Arc<AtomicPosition>, - len: Option<u64>, - pub(crate) tick: u64, - pub(crate) started: Instant, - status: Status, - est: Estimator, - pub(crate) message: TabExpandedString, - pub(crate) prefix: TabExpandedString, -} - -impl ProgressState { - pub(crate) fn new(len: Option<u64>, pos: Arc<AtomicPosition>) -> Self { - let now = Instant::now(); - Self { - pos, - len, - tick: 0, - status: Status::InProgress, - started: now, - est: Estimator::new(now), - message: TabExpandedString::NoTabs("".into()), - prefix: TabExpandedString::NoTabs("".into()), - } - } - - /// Indicates that the progress bar finished. - pub fn is_finished(&self) -> bool { - match self.status { - Status::InProgress => false, - Status::DoneVisible => true, - Status::DoneHidden => true, - } - } - - /// Returns the completion as a floating-point number between 0 and 1 - pub fn fraction(&self) -> f32 { - let pos = self.pos.pos.load(Ordering::Relaxed); - let pct = match (pos, self.len) { - (_, None) => 0.0, - (_, Some(0)) => 1.0, - (0, _) => 0.0, - (pos, Some(len)) => pos as f32 / len as f32, - }; - pct.clamp(0.0, 1.0) - } - - /// The expected ETA - pub fn eta(&self) -> Duration { - if self.is_finished() { - return Duration::new(0, 0); - } - - let len = match self.len { - Some(len) => len, - None => return Duration::new(0, 0), - }; - - let pos = self.pos.pos.load(Ordering::Relaxed); - - let sps = self.est.steps_per_second(Instant::now()); - - // Infinite duration should only ever happen at the beginning, so in this case it's okay to - // just show an ETA of 0 until progress starts to occur. - if sps == 0.0 { - return Duration::new(0, 0); - } - - secs_to_duration(len.saturating_sub(pos) as f64 / sps) - } - - /// The expected total duration (that is, elapsed time + expected ETA) - pub fn duration(&self) -> Duration { - if self.len.is_none() || self.is_finished() { - return Duration::new(0, 0); - } - self.started.elapsed().saturating_add(self.eta()) - } - - /// The number of steps per second - pub fn per_sec(&self) -> f64 { - if let Status::InProgress = self.status { - self.est.steps_per_second(Instant::now()) - } else { - let len = self.len.unwrap_or_else(|| self.pos()); - len as f64 / self.started.elapsed().as_secs_f64() - } - } - - pub fn elapsed(&self) -> Duration { - self.started.elapsed() - } - - pub fn pos(&self) -> u64 { - self.pos.pos.load(Ordering::Relaxed) - } - - pub fn set_pos(&mut self, pos: u64) { - self.pos.set(pos); - } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> Option<u64> { - self.len - } - - pub fn set_len(&mut self, len: u64) { - self.len = Some(len); - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub(crate) enum TabExpandedString { - NoTabs(Cow<'static, str>), - WithTabs { - original: Cow<'static, str>, - expanded: String, - tab_width: usize, - }, -} - -impl TabExpandedString { - pub(crate) fn new(s: Cow<'static, str>, tab_width: usize) -> Self { - let expanded = s.replace('\t', &" ".repeat(tab_width)); - if s == expanded { - Self::NoTabs(s) - } else { - Self::WithTabs { - original: s, - expanded, - tab_width, - } - } - } - - pub(crate) fn expanded(&self) -> &str { - match &self { - Self::NoTabs(s) => { - debug_assert!(!s.contains('\t')); - s - } - Self::WithTabs { expanded, .. } => expanded, - } - } - - pub(crate) fn set_tab_width(&mut self, new_tab_width: usize) { - if let Self::WithTabs { - original, - expanded, - tab_width, - } = self - { - if *tab_width != new_tab_width { - *tab_width = new_tab_width; - *expanded = original.replace('\t', &" ".repeat(new_tab_width)); - } - } - } -} - -/// Double-smoothed exponentially weighted estimator -/// -/// This uses an exponentially weighted *time-based* estimator, meaning that it exponentially -/// downweights old data based on its age. The rate at which this occurs is currently a constant -/// value of 15 seconds for 90% weighting. This means that all data older than 15 seconds has a -/// collective weight of 0.1 in the estimate, and all data older than 30 seconds has a collective -/// weight of 0.01, and so on. -/// -/// The primary value exposed by `Estimator` is `steps_per_second`. This value is doubly-smoothed, -/// meaning that is the result of using an exponentially weighted estimator (as described above) to -/// estimate the value of another exponentially weighted estimator, which estimates the value of -/// the raw data. -/// -/// The purpose of this extra smoothing step is to reduce instantaneous fluctations in the estimate -/// when large updates are received. Without this, estimates might have a large spike followed by a -/// slow asymptotic approach to zero (until the next spike). -#[derive(Debug)] -pub(crate) struct Estimator { - smoothed_steps_per_sec: f64, - double_smoothed_steps_per_sec: f64, - prev_steps: u64, - prev_time: Instant, - start_time: Instant, -} - -impl Estimator { - fn new(now: Instant) -> Self { - Self { - smoothed_steps_per_sec: 0.0, - double_smoothed_steps_per_sec: 0.0, - prev_steps: 0, - prev_time: now, - start_time: now, - } - } - - fn record(&mut self, new_steps: u64, now: Instant) { - // sanity check: don't record data if time or steps have not advanced - if new_steps <= self.prev_steps || now <= self.prev_time { - // Reset on backwards seek to prevent breakage from seeking to the end for length determination - // See https://github.com/console-rs/indicatif/issues/480 - if new_steps < self.prev_steps { - self.prev_steps = new_steps; - self.reset(now); - } - return; - } - - let delta_steps = new_steps - self.prev_steps; - let delta_t = duration_to_secs(now - self.prev_time); - - // the rate of steps we saw in this update - let new_steps_per_second = delta_steps as f64 / delta_t; - - // update the estimate: a weighted average of the old estimate and new data - let weight = estimator_weight(delta_t); - self.smoothed_steps_per_sec = - self.smoothed_steps_per_sec * weight + new_steps_per_second * (1.0 - weight); - - // An iterative estimate like `smoothed_steps_per_sec` is supposed to be an exponentially - // weighted average from t=0 back to t=-inf; Since we initialize it to 0, we neglect the - // (non-existent) samples in the weighted average prior to the first one, so the resulting - // average must be normalized. We normalize the single estimate here in order to use it as - // a source for the double smoothed estimate. See comment on normalization in - // `steps_per_second` for details. - let delta_t_start = duration_to_secs(now - self.start_time); - let total_weight = 1.0 - estimator_weight(delta_t_start); - let normalized_smoothed_steps_per_sec = self.smoothed_steps_per_sec / total_weight; - - // determine the double smoothed value (EWA smoothing of the single EWA) - self.double_smoothed_steps_per_sec = self.double_smoothed_steps_per_sec * weight - + normalized_smoothed_steps_per_sec * (1.0 - weight); - - self.prev_steps = new_steps; - self.prev_time = now; - } - - /// Reset the state of the estimator. Once reset, estimates will not depend on any data prior - /// to `now`. This does not reset the stored position of the progress bar. - pub(crate) fn reset(&mut self, now: Instant) { - self.smoothed_steps_per_sec = 0.0; - self.double_smoothed_steps_per_sec = 0.0; - - // only reset prev_time, not prev_steps - self.prev_time = now; - self.start_time = now; - } - - /// Average time per step in seconds, using double exponential smoothing - fn steps_per_second(&self, now: Instant) -> f64 { - // Because the value stored in the Estimator is only updated when the Estimator receives an - // update, this value will become stuck if progress stalls. To return an accurate estimate, - // we determine how much time has passed since the last update, and treat this as a - // pseudo-update with 0 steps. - let delta_t = duration_to_secs(now - self.prev_time); - let reweight = estimator_weight(delta_t); - - // Normalization of estimates: - // - // The raw estimate is a single value (smoothed_steps_per_second) that is iteratively - // updated. At each update, the previous value of the estimate is downweighted according to - // its age, receiving the iterative weight W(t) = 0.1 ^ (t/15). - // - // Since W(Sum(t_n)) = Prod(W(t_n)), the total weight of a sample after a series of - // iterative steps is simply W(t_e) - W(t_b), where t_e is the time since the end of the - // sample, and t_b is the time since the beginning. The resulting estimate is therefore a - // weighted average with sample weights W(t_e) - W(t_b). - // - // Notice that the weighting function generates sample weights that sum to 1 only when the - // sample times span from t=0 to t=inf; but this is not the case. We have a first sample - // with finite, positive t_b = t_f. In the raw estimate, we handle times prior to t_f by - // setting an initial value of 0, meaning that these (non-existent) samples have no weight. - // - // Therefore, the raw estimate must be normalized by dividing it by the sum of the weights - // in the weighted average. This sum is just W(0) - W(t_f), where t_f is the time since the - // first sample, and W(0) = 1. - let delta_t_start = duration_to_secs(now - self.start_time); - let total_weight = 1.0 - estimator_weight(delta_t_start); - - // Generate updated values for `smoothed_steps_per_sec` and `double_smoothed_steps_per_sec` - // (sps and dsps) without storing them. Note that we normalize sps when using it as a - // source to update dsps, and then normalize dsps itself before returning it. - let sps = self.smoothed_steps_per_sec * reweight / total_weight; - let dsps = self.double_smoothed_steps_per_sec * reweight + sps * (1.0 - reweight); - dsps / total_weight - } -} - -pub(crate) struct AtomicPosition { - pub(crate) pos: AtomicU64, - capacity: AtomicU8, - prev: AtomicU64, - start: Instant, -} - -impl AtomicPosition { - pub(crate) fn new() -> Self { - Self { - pos: AtomicU64::new(0), - capacity: AtomicU8::new(MAX_BURST), - prev: AtomicU64::new(0), - start: Instant::now(), - } - } - - pub(crate) fn allow(&self, now: Instant) -> bool { - if now < self.start { - return false; - } - - let mut capacity = self.capacity.load(Ordering::Acquire); - // `prev` is the number of ms after `self.started` we last returned `true`, in ns - let prev = self.prev.load(Ordering::Acquire); - // `elapsed` is the number of ns since `self.started` - let elapsed = (now - self.start).as_nanos() as u64; - // `diff` is the number of ns since we last returned `true` - let diff = elapsed.saturating_sub(prev); - - // If `capacity` is 0 and not enough time (1ms) has passed since `prev` - // to add new capacity, return `false`. The goal of this method is to - // make this decision as efficient as possible. - if capacity == 0 && diff < INTERVAL { - return false; - } - - // We now calculate `new`, the number of ms, in ns, since we last returned `true`, - // and `remainder`, which represents a number of ns less than 1ms which we cannot - // convert into capacity now, so we're saving it for later. We do this by - // substracting this from `elapsed` before storing it into `self.prev`. - let (new, remainder) = ((diff / INTERVAL), (diff % INTERVAL)); - // We add `new` to `capacity`, subtract one for returning `true` from here, - // then make sure it does not exceed a maximum of `MAX_BURST`. - capacity = Ord::min(MAX_BURST as u128, (capacity as u128) + (new as u128) - 1) as u8; - - // Then, we just store `capacity` and `prev` atomically for the next iteration - self.capacity.store(capacity, Ordering::Release); - self.prev.store(elapsed - remainder, Ordering::Release); - true - } - - fn reset(&self, now: Instant) { - self.set(0); - let elapsed = (now.saturating_duration_since(self.start)).as_millis() as u64; - self.prev.store(elapsed, Ordering::Release); - } - - pub(crate) fn inc(&self, delta: u64) { - self.pos.fetch_add(delta, Ordering::SeqCst); - } - - pub(crate) fn set(&self, pos: u64) { - self.pos.store(pos, Ordering::Release); - } -} - -const INTERVAL: u64 = 1_000_000; -const MAX_BURST: u8 = 10; - -/// Behavior of a progress bar when it is finished -/// -/// This is invoked when a [`ProgressBar`] or [`ProgressBarIter`] completes and -/// [`ProgressBar::is_finished`] is false. -/// -/// [`ProgressBar`]: crate::ProgressBar -/// [`ProgressBarIter`]: crate::ProgressBarIter -/// [`ProgressBar::is_finished`]: crate::ProgressBar::is_finished -#[derive(Clone, Debug)] -pub enum ProgressFinish { - /// Finishes the progress bar and leaves the current message - /// - /// Same behavior as calling [`ProgressBar::finish()`](crate::ProgressBar::finish). - AndLeave, - /// Finishes the progress bar and sets a message - /// - /// Same behavior as calling [`ProgressBar::finish_with_message()`](crate::ProgressBar::finish_with_message). - WithMessage(Cow<'static, str>), - /// Finishes the progress bar and completely clears it (this is the default) - /// - /// Same behavior as calling [`ProgressBar::finish_and_clear()`](crate::ProgressBar::finish_and_clear). - AndClear, - /// Finishes the progress bar and leaves the current message and progress - /// - /// Same behavior as calling [`ProgressBar::abandon()`](crate::ProgressBar::abandon). - Abandon, - /// Finishes the progress bar and sets a message, and leaves the current progress - /// - /// Same behavior as calling [`ProgressBar::abandon_with_message()`](crate::ProgressBar::abandon_with_message). - AbandonWithMessage(Cow<'static, str>), -} - -impl Default for ProgressFinish { - fn default() -> Self { - Self::AndClear - } -} - -/// Get the appropriate dilution weight for Estimator data given the data's age (in seconds) -/// -/// Whenever an update occurs, we will create a new estimate using a weight `w_i` like so: -/// -/// ```math -/// <new estimate> = <previous estimate> * w_i + <new data> * (1 - w_i) -/// ``` -/// -/// In other words, the new estimate is a weighted average of the previous estimate and the new -/// data. We want to choose weights such that for any set of samples where `t_0, t_1, ...` are -/// the durations of the samples: -/// -/// ```math -/// Sum(t_i) = ews ==> Prod(w_i) = 0.1 -/// ``` -/// -/// With this constraint it is easy to show that -/// -/// ```math -/// w_i = 0.1 ^ (t_i / ews) -/// ``` -/// -/// Notice that the constraint implies that estimates are independent of the durations of the -/// samples, a very useful feature. -fn estimator_weight(age: f64) -> f64 { - const EXPONENTIAL_WEIGHTING_SECONDS: f64 = 15.0; - 0.1_f64.powf(age / EXPONENTIAL_WEIGHTING_SECONDS) -} - -fn duration_to_secs(d: Duration) -> f64 { - d.as_secs() as f64 + f64::from(d.subsec_nanos()) / 1_000_000_000f64 -} - -fn secs_to_duration(s: f64) -> Duration { - let secs = s.trunc() as u64; - let nanos = (s.fract() * 1_000_000_000f64) as u32; - Duration::new(secs, nanos) -} - -#[derive(Debug)] -pub(crate) enum Status { - InProgress, - DoneVisible, - DoneHidden, -} - -pub(crate) const DEFAULT_TAB_WIDTH: usize = 8; - -#[cfg(test)] -mod tests { - use super::*; - use crate::ProgressBar; - - // https://github.com/rust-lang/rust-clippy/issues/10281 - #[allow(clippy::uninlined_format_args)] - #[test] - fn test_steps_per_second() { - let test_rate = |items_per_second| { - let mut now = Instant::now(); - let mut est = Estimator::new(now); - let mut pos = 0; - - for _ in 0..20 { - pos += items_per_second; - now += Duration::from_secs(1); - est.record(pos, now); - } - let avg_steps_per_second = est.steps_per_second(now); - - assert!(avg_steps_per_second > 0.0); - assert!(avg_steps_per_second.is_finite()); - - let absolute_error = (avg_steps_per_second - items_per_second as f64).abs(); - let relative_error = absolute_error / items_per_second as f64; - assert!( - relative_error < 1.0 / 1e9, - "Expected rate: {}, actual: {}, relative error: {}", - items_per_second, - avg_steps_per_second, - relative_error - ); - }; - - test_rate(1); - test_rate(1_000); - test_rate(1_000_000); - test_rate(1_000_000_000); - test_rate(1_000_000_001); - test_rate(100_000_000_000); - test_rate(1_000_000_000_000); - test_rate(100_000_000_000_000); - test_rate(1_000_000_000_000_000); - } - - #[test] - fn test_double_exponential_ave() { - let mut now = Instant::now(); - let mut est = Estimator::new(now); - let mut pos = 0; - - // note: this is the default weight set in the Estimator - let weight = 15; - - for _ in 0..weight { - pos += 1; - now += Duration::from_secs(1); - est.record(pos, now); - } - now += Duration::from_secs(weight); - - // The first level EWA: - // -> 90% weight @ 0 eps, 9% weight @ 1 eps, 1% weight @ 0 eps - // -> then normalized by deweighting the 1% weight (before -30 seconds) - let single_target = 0.09 / 0.99; - - // The second level EWA: - // -> same logic as above, but using the first level EWA as the source - let double_target = (0.9 * single_target + 0.09) / 0.99; - assert_eq!(est.steps_per_second(now), double_target); - } - - #[test] - fn test_estimator_rewind_position() { - let mut now = Instant::now(); - let mut est = Estimator::new(now); - - now += Duration::from_secs(1); - est.record(1, now); - - // should not panic - now += Duration::from_secs(1); - est.record(0, now); - - // check that reset occurred (estimator at 1 event per sec) - now += Duration::from_secs(1); - est.record(1, now); - assert_eq!(est.steps_per_second(now), 1.0); - - // check that progress bar handles manual seeking - let pb = ProgressBar::hidden(); - pb.set_length(10); - pb.set_position(1); - pb.tick(); - // Should not panic. - pb.set_position(0); - } - - #[test] - fn test_reset_eta() { - let mut now = Instant::now(); - let mut est = Estimator::new(now); - - // two per second, then reset - now += Duration::from_secs(1); - est.record(2, now); - est.reset(now); - - // now one per second, and verify - now += Duration::from_secs(1); - est.record(3, now); - assert_eq!(est.steps_per_second(now), 1.0); - } - - #[test] - fn test_duration_stuff() { - let duration = Duration::new(42, 100_000_000); - let secs = duration_to_secs(duration); - assert_eq!(secs_to_duration(secs), duration); - } - - #[test] - fn test_atomic_position_large_time_difference() { - let atomic_position = AtomicPosition::new(); - let later = atomic_position.start + Duration::from_nanos(INTERVAL * u64::from(u8::MAX)); - // Should not panic. - atomic_position.allow(later); - } -} 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"); - } -} diff --git a/vendor/indicatif/src/term_like.rs b/vendor/indicatif/src/term_like.rs deleted file mode 100644 index b489b65..0000000 --- a/vendor/indicatif/src/term_like.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::fmt::Debug; -use std::io; - -use console::Term; - -/// A trait for minimal terminal-like behavior. -/// -/// Anything that implements this trait can be used a draw target via [`ProgressDrawTarget::term_like`]. -/// -/// [`ProgressDrawTarget::term_like`]: crate::ProgressDrawTarget::term_like -pub trait TermLike: Debug + Send + Sync { - /// Return the terminal width - fn width(&self) -> u16; - /// Return the terminal height - fn height(&self) -> u16 { - // FIXME: remove this default impl in the next major version bump - 20 // sensible default - } - - /// Move the cursor up by `n` lines - fn move_cursor_up(&self, n: usize) -> io::Result<()>; - /// Move the cursor down by `n` lines - fn move_cursor_down(&self, n: usize) -> io::Result<()>; - /// Move the cursor right by `n` chars - fn move_cursor_right(&self, n: usize) -> io::Result<()>; - /// Move the cursor left by `n` chars - fn move_cursor_left(&self, n: usize) -> io::Result<()>; - - /// Write a string and add a newline. - fn write_line(&self, s: &str) -> io::Result<()>; - /// Write a string - fn write_str(&self, s: &str) -> io::Result<()>; - /// Clear the current line and reset the cursor to beginning of the line - fn clear_line(&self) -> io::Result<()>; - - fn flush(&self) -> io::Result<()>; -} - -impl TermLike for Term { - fn width(&self) -> u16 { - self.size().1 - } - - fn height(&self) -> u16 { - self.size().0 - } - - fn move_cursor_up(&self, n: usize) -> io::Result<()> { - self.move_cursor_up(n) - } - - fn move_cursor_down(&self, n: usize) -> io::Result<()> { - self.move_cursor_down(n) - } - - fn move_cursor_right(&self, n: usize) -> io::Result<()> { - self.move_cursor_right(n) - } - - fn move_cursor_left(&self, n: usize) -> io::Result<()> { - self.move_cursor_left(n) - } - - fn write_line(&self, s: &str) -> io::Result<()> { - self.write_line(s) - } - - fn write_str(&self, s: &str) -> io::Result<()> { - self.write_str(s) - } - - fn clear_line(&self) -> io::Result<()> { - self.clear_line() - } - - fn flush(&self) -> io::Result<()> { - self.flush() - } -} |