diff --git a/Cargo.toml b/Cargo.toml index d318b89..f3e3875 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ include = ["src/**/*", "README.md"] lazy_static = { version = "1.4.0" } maplit = { version = "1.0.2" } strum = { version = "0.24.0", features = ["derive"] } +yansi = { version = "0.5.1" } diff --git a/README.md b/README.md index e2e7335..cd990b0 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ use std::thread::sleep; use std::time::Duration; fn main() { - let mut sp = Spinner::new(Spinners::Dots9, "Waiting for 3 seconds".into()); + let mut sp = Spinner::new(Spinners::Dots9, "Waiting for 3 seconds".into(), None); sleep(Duration::from_secs(3)); sp.stop(); } diff --git a/examples/color.rs b/examples/color.rs new file mode 100644 index 0000000..e9a0cf2 --- /dev/null +++ b/examples/color.rs @@ -0,0 +1,12 @@ +use spinners::{Color, Spinner, Spinners}; +use std::{thread::sleep, time::Duration}; + +fn main() { + let mut sp = Spinner::new_with_color( + Spinners::Dots9, + "Waiting for 3 seconds".into(), + Color::Green, + ); + sleep(Duration::from_secs(3)); + sp.stop_with_message("Finishing waiting for 3 seconds".into()); +} diff --git a/examples/cycle.rs b/examples/cycle.rs index d5f7ae8..079a482 100644 --- a/examples/cycle.rs +++ b/examples/cycle.rs @@ -5,7 +5,7 @@ use strum::IntoEnumIterator; fn main() { // loop through each spinner and display them for 2 seconds for spinner in Spinners::iter() { - let mut sp = Spinner::new(spinner.clone(), format!("{:?}", spinner)); + let mut sp = Spinner::new(spinner, format!("{:?}", spinner)); sleep(Duration::from_secs(2)); sp.stop(); } diff --git a/examples/stop_persist.rs b/examples/stop_persist.rs index 09f16ee..d00a472 100644 --- a/examples/stop_persist.rs +++ b/examples/stop_persist.rs @@ -10,5 +10,5 @@ fn main() { "Waiting for 3 seconds".into(), ); sleep(Duration::from_secs(3)); - sp.stop_and_persist("✔", "That worked!".to_string()) + sp.stop_and_persist("✔", "That worked!".to_string()); } diff --git a/examples/timer.rs b/examples/timer.rs index d7d5ec1..03d35bd 100644 --- a/examples/timer.rs +++ b/examples/timer.rs @@ -8,6 +8,7 @@ fn main() { let mut sp = Spinner::with_timer( Spinners::from_str(&spinner_name).unwrap(), "Waiting for 3 seconds".into(), + None, ); sleep(Duration::from_secs(3)); sp.stop_with_newline(); diff --git a/src/lib.rs b/src/lib.rs index 9dc456e..6cbc913 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,8 @@ use std::{ pub use crate::utils::spinner_names::SpinnerNames as Spinners; use crate::utils::spinners_data::SPINNERS as SpinnersMap; -pub use crate::utils::stream::Stream; - +pub use crate::utils::color::Color; +use crate::utils::color::colorize; mod utils; pub struct Spinner { @@ -47,47 +47,43 @@ impl Spinner { /// /// let sp = Spinner::new(Spinners::Dots, String::new()); /// ``` - pub fn new(spinner: Spinners, message: String) -> Self { + #[must_use] + pub fn new(spinner: Spinners, message: String) -> Self { Self::new_inner(spinner, message, None, None) } - /// Create a new spinner that logs the time since it was created - pub fn with_timer(spinner: Spinners, message: String) -> Self { - Self::new_inner(spinner, message, Some(Instant::now()), None) - } - - /// Creates a new spinner along with a message with a specified output stream + /// Create a new colored spinner along with a message /// /// # Examples /// /// Basic Usage: /// /// ``` - /// use spinners::{Spinner, Spinners, Stream}; - /// - /// let sp = Spinner::with_stream(Spinners::Dots, String::new(), Stream::Stderr); - /// ``` - pub fn with_stream(spinner: Spinners, message: String, stream: Stream) -> Self { - Self::new_inner(spinner, message, None, Some(stream)) - } - - /// Creates a new spinner that logs the time since it was created with a specified output stream + /// use spinners::{Spinner, Spinners, Color}; /// - /// # Examples + /// let sp = Spinner::new_with_color(Spinners::Dots, "Loading things into memory...".into(), Color::Blue); + /// ``` /// - /// Basic Usage: + /// No Message: /// /// ``` - /// use spinners::{Spinner, Spinners, Stream}; - /// - /// let sp = Spinner::with_timer_and_stream(Spinners::Dots, String::new(), Stream::Stderr); + /// use spinners::{Spinner, Spinners}; + /// + /// let sp = Spinner::new_with_color(Spinners::Dots, String::new(), None); /// ``` - pub fn with_timer_and_stream(spinner: Spinners, message: String, stream: Stream) -> Self { - Self::new_inner(spinner, message, Some(Instant::now()), Some(stream)) + pub fn new_with_color(spinner: Spinners, message: String, color: T) -> Self + where T: Into> + std::marker::Send + 'static + std::marker::Copy { + Self::new_inner(spinner, message, color, None) } - fn new_inner(spinner: Spinners, message: String, start_time: Option, stream: Option) -> Self - { + /// Create a new spinner that logs the time since it was created + pub fn with_timer(spinner: Spinners, message: String, color: T) -> Self + where T: Into> + std::marker::Send + 'static + std::marker::Copy { + Self::new_inner(spinner, message, color, Some(Instant::now())) + } + + fn new_inner(spinner: Spinners, message: String, color: T, start_time: Option) -> Self + where T: Into> + std::marker::Send + 'static + std::marker::Copy { let spinner_name = spinner.to_string(); let spinner_data = SpinnersMap .get(&spinner_name) @@ -98,15 +94,25 @@ impl Spinner { let (sender, recv) = channel::<(Instant, Option)>(); let join = thread::spawn(move || 'outer: loop { - - for frame in spinner_data.frames.iter() { + let mut stdout = stdout(); + for frame in &spinner_data.frames { let (do_stop, stop_time, stop_symbol) = match recv.try_recv() { Ok((stop_time, stop_symbol)) => (true, Some(stop_time), stop_symbol), Err(TryRecvError::Disconnected) => (true, None, None), Err(TryRecvError::Empty) => (false, None, None), }; - let frame = stop_symbol.unwrap_or_else(|| frame.to_string()); + let frame = stop_symbol.unwrap_or_else(|| (*frame).to_string()); + match start_time { + None => { + print!("\r{} {}", colorize(frame, color.into()), message); + } + Some(start_time) => { + let now = stop_time.unwrap_or_else(Instant::now); + let duration = now.duration_since(start_time).as_secs_f64(); + print!("\r{}{:>10.3} s\t{}", colorize(frame, color.into()), duration, message); + } + } stream.write(&frame, &message, start_time, stop_time).expect("IO Error"); @@ -114,7 +120,7 @@ impl Spinner { break 'outer; } - thread::sleep(Duration::from_millis(spinner_data.interval as u64)); + thread::sleep(Duration::from_millis(u64::from(spinner_data.interval))); } }); diff --git a/src/utils/color.rs b/src/utils/color.rs new file mode 100644 index 0000000..ea2e22f --- /dev/null +++ b/src/utils/color.rs @@ -0,0 +1,27 @@ +use yansi::Paint; + +#[derive(Debug, Clone, Copy)] +pub enum Color { + Blue, + Green, + Red, + Yellow, + Cyan, + White, + Magenta, + Black, +} + +pub fn colorize(input: String, color: Option) -> Paint { + match color { + Some(Color::Blue) => Paint::blue(input), + Some(Color::Green) => Paint::green(input), + Some(Color::Red) => Paint::red(input), + Some(Color::Yellow) => Paint::yellow(input), + Some(Color::Cyan) => Paint::cyan(input), + Some(Color::White) => Paint::white(input), + Some(Color::Magenta) => Paint::magenta(input), + Some(Color::Black) => Paint::black(input), + None => Paint::new(input), + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 6f2b356..0cbc773 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod color; pub mod spinner_data; pub mod spinner_names; pub mod spinners_data; diff --git a/src/utils/spinner_names.rs b/src/utils/spinner_names.rs index a954ca4..4ba6f22 100644 --- a/src/utils/spinner_names.rs +++ b/src/utils/spinner_names.rs @@ -1,6 +1,6 @@ use strum::{Display, EnumIter, EnumString}; -#[derive(Debug, Clone, EnumIter, Display, EnumString)] +#[derive(Debug, Copy, Clone, EnumIter, Display, EnumString)] pub enum SpinnerNames { Dots, Dots2,