diff --git a/src/progress_bar.rs b/src/progress_bar.rs index b5a3915c..743d560c 100644 --- a/src/progress_bar.rs +++ b/src/progress_bar.rs @@ -3,8 +3,9 @@ use std::borrow::Cow; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Condvar, Mutex, MutexGuard, Weak}; use std::time::{Duration, Instant}; -use std::{fmt, io, mem, thread}; +use std::{fmt, io, thread}; +use console::strip_ansi_codes; #[cfg(test)] use once_cell::sync::Lazy; @@ -71,13 +72,13 @@ impl ProgressBar { /// A convenience builder-like function for a progress bar with a given prefix pub fn with_prefix(self, prefix: impl Into>) -> ProgressBar { - self.state().style.prefix = prefix.into(); + self.state().state.prefix = prefix.into(); self } /// A convenience builder-like function for a progress bar with a given message pub fn with_message(self, message: impl Into>) -> ProgressBar { - self.state().style.message = message.into(); + self.state().state.message = message.into(); self } @@ -121,11 +122,8 @@ impl ProgressBar { /// Overrides the stored style /// /// This does not redraw the bar. Call [`ProgressBar::tick()`] to force it. - pub fn set_style(&self, mut style: ProgressStyle) { - let mut state = self.state(); - mem::swap(&mut state.style.message, &mut style.message); - mem::swap(&mut state.style.prefix, &mut style.prefix); - state.style = style; + pub fn set_style(&self, style: ProgressStyle) { + self.state().style = style; } /// Spawns a background thread to tick the progress bar @@ -517,6 +515,26 @@ impl ProgressBar { self.state().draw_target.remote().map(|(_, idx)| idx) } + /// Current message + pub fn message(&self) -> Cow<'static, str> { + self.state().state.message.clone() + } + + /// Current message (with ANSI escape codes stripped) + pub fn message_unstyled(&self) -> String { + strip_ansi_codes(&self.message()).to_string() + } + + /// Current prefix + pub fn prefix(&self) -> Cow<'static, str> { + self.state().state.prefix.clone() + } + + /// Current prefix (with ANSI escape codes stripped) + pub fn prefix_unstyled(&self) -> String { + strip_ansi_codes(&self.prefix()).to_string() + } + #[inline] pub(crate) fn state(&self) -> MutexGuard<'_, BarState> { self.state.lock().unwrap() @@ -644,6 +662,7 @@ pub(crate) static TICKER_TEST: Lazy> = Lazy::new(Mutex::default); #[cfg(test)] mod tests { use super::*; + use console::Style; #[allow(clippy::float_cmp)] #[test] @@ -743,4 +762,20 @@ mod tests { drop(pb2); assert!(!TICKER_RUNNING.load(Ordering::SeqCst)); } + + #[test] + fn access_message_and_prefix() { + let pb = ProgressBar::new(80); + pb.set_message(Style::new().red().bold().apply_to("text").to_string()); + pb.set_prefix( + Style::new() + .on_blue() + .italic() + .apply_to("prefix!") + .to_string(), + ); + + assert_eq!(pb.message_unstyled(), "text"); + assert_eq!(pb.prefix_unstyled(), "prefix!"); + } } diff --git a/src/state.rs b/src/state.rs index 964d98f8..b1d93531 100644 --- a/src/state.rs +++ b/src/state.rs @@ -42,7 +42,7 @@ impl BarState { if let Some(len) = self.state.len { self.state.pos.set(len); } - self.style.message = msg; + self.state.message = msg; } ProgressFinish::AndClear => { if let Some(len) = self.state.len { @@ -51,7 +51,7 @@ impl BarState { self.state.status = Status::DoneHidden; } ProgressFinish::Abandon => {} - ProgressFinish::AbandonWithMessage(msg) => self.style.message = msg, + ProgressFinish::AbandonWithMessage(msg) => self.state.message = msg, } // There's no need to update the estimate here; once the `status` is no longer @@ -93,12 +93,12 @@ impl BarState { } pub(crate) fn set_message(&mut self, now: Instant, msg: Cow<'static, str>) { - self.style.message = msg; + self.state.message = msg; self.update_estimate_and_draw(now); } pub(crate) fn set_prefix(&mut self, now: Instant, prefix: Cow<'static, str>) { - self.style.prefix = prefix; + self.state.prefix = prefix; self.update_estimate_and_draw(now); } @@ -190,6 +190,8 @@ pub struct ProgressState { pub(crate) started: Instant, status: Status, est: Estimator, + pub(crate) message: Cow<'static, str>, + pub(crate) prefix: Cow<'static, str>, } impl ProgressState { @@ -201,6 +203,8 @@ impl ProgressState { status: Status::InProgress, started: Instant::now(), est: Estimator::new(Instant::now()), + message: "".into(), + prefix: "".into(), } } diff --git a/src/style.rs b/src/style.rs index e8cd0860..ccadffc0 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; + use std::collections::HashMap; use std::fmt::{self, Write}; use std::mem; @@ -15,8 +15,6 @@ use crate::state::ProgressState; /// Controls the rendering style of progress bars #[derive(Clone)] pub struct ProgressStyle { - pub(crate) message: Cow<'static, str>, - pub(crate) prefix: Cow<'static, str>, tick_strings: Vec>, progress_chars: Vec>, template: Template, @@ -81,8 +79,6 @@ impl ProgressStyle { let progress_chars = segment("█░"); let char_width = width(&progress_chars); Self { - message: "".into(), - prefix: "".into(), tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ " .chars() .map(|c| c.to_string().into()) @@ -258,8 +254,8 @@ impl ProgressStyle { wide = Some(WideElement::Message { align }); buf.push('\x00'); } - "msg" => buf.push_str(&self.message), - "prefix" => buf.push_str(&self.prefix), + "msg" => buf.push_str(&state.message), + "prefix" => buf.push_str(&state.prefix), "pos" => buf.write_fmt(format_args!("{}", pos)).unwrap(), "human_pos" => { buf.write_fmt(format_args!("{}", HumanCount(pos))).unwrap() @@ -392,7 +388,7 @@ impl<'a> WideElement<'a> { buf.write_fmt(format_args!( "{}", PaddedStringDisplay { - str: &style.message, + str: &state.message, width: left, align: *align, truncate: true, @@ -558,7 +554,7 @@ impl fmt::Display for TemplateError { impl std::error::Error for TemplateError {} -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Eq)] enum TemplatePart { Literal(String), Placeholder { @@ -572,7 +568,7 @@ enum TemplatePart { NewLine, } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] enum State { Literal, MaybeOpen, @@ -733,23 +729,23 @@ mod tests { fn align_truncation() { const WIDTH: u16 = 10; let pos = Arc::new(AtomicPosition::new()); - let state = ProgressState::new(Some(10), pos); + let mut state = ProgressState::new(Some(10), pos); let mut buf = Vec::new(); - let mut style = ProgressStyle::with_template("{wide_msg}").unwrap(); - style.message = "abcdefghijklmnopqrst".into(); + let style = ProgressStyle::with_template("{wide_msg}").unwrap(); + state.message = "abcdefghijklmnopqrst".into(); style.format_state(&state, &mut buf, WIDTH); assert_eq!(&buf[0], "abcdefghij"); buf.clear(); - let mut style = ProgressStyle::with_template("{wide_msg:>}").unwrap(); - style.message = "abcdefghijklmnopqrst".into(); + let style = ProgressStyle::with_template("{wide_msg:>}").unwrap(); + state.message = "abcdefghijklmnopqrst".into(); style.format_state(&state, &mut buf, WIDTH); assert_eq!(&buf[0], "klmnopqrst"); buf.clear(); - let mut style = ProgressStyle::with_template("{wide_msg:^}").unwrap(); - style.message = "abcdefghijklmnopqrst".into(); + let style = ProgressStyle::with_template("{wide_msg:^}").unwrap(); + state.message = "abcdefghijklmnopqrst".into(); style.format_state(&state, &mut buf, WIDTH); assert_eq!(&buf[0], "fghijklmno"); } @@ -761,7 +757,7 @@ mod tests { let pos = Arc::new(AtomicPosition::new()); // half finished pos.set(2); - let state = ProgressState::new(Some(4), pos); + let mut state = ProgressState::new(Some(4), pos); let mut buf = Vec::new(); let style = ProgressStyle::with_template("{wide_bar}") @@ -781,8 +777,8 @@ mod tests { ); buf.clear(); - let mut style = ProgressStyle::with_template("{wide_msg:^.red.on_blue}").unwrap(); - style.message = "foobar".into(); + let style = ProgressStyle::with_template("{wide_msg:^.red.on_blue}").unwrap(); + state.message = "foobar".into(); style.format_state(&state, &mut buf, WIDTH); assert_eq!(&buf[0], "\u{1b}[31m\u{1b}[44m foobar \u{1b}[0m"); }