Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: spinner - expose SpinnerStyle and add example #28

Merged
merged 1 commit into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions examples/spinner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ use std::{thread::sleep, time::Duration};
use demand::{Spinner, SpinnerStyle};

fn main() {
let custom_style = SpinnerStyle {
frames: vec![
" ", "- ", "-- ", "---", " --", " -", " ", " -", " --", "---", "-- ", "- ",
],
fps: Duration::from_millis(1000 / 10),
};

Spinner::new("Loading Data...")
.style(SpinnerStyle::line())
.style(&custom_style)
.run(|| {
sleep(Duration::from_secs(2));
})
.expect("error running spinner");
println!("Done!");
println!("Data loaded.");
}
50 changes: 31 additions & 19 deletions src/spinner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
};

use console::Term;
use once_cell::sync::Lazy;
use termcolor::{Buffer, WriteColor};

use crate::{theme, Theme};
Expand All @@ -18,7 +19,7 @@ use crate::{theme, Theme};
/// use std::thread::sleep;
///
/// let spinner = Spinner::new("Loading data...")
/// .style(SpinnerStyle::line())
/// .style(&SpinnerStyle::line())
/// .run(|| {
/// sleep(Duration::from_secs(2));
/// })
Expand All @@ -28,7 +29,7 @@ pub struct Spinner<'a> {
// The title of the spinner
pub title: String,
// The style of the spinner
pub style: SpinnerStyle,
pub style: &'a SpinnerStyle,
/// The colors/style of the spinner
pub theme: &'a Theme,

Expand All @@ -42,16 +43,16 @@ impl<'a> Spinner<'a> {
pub fn new<S: Into<String>>(title: S) -> Self {
Self {
title: title.into(),
style: SpinnerStyle::line(),
theme: &*theme::DEFAULT,
style: &DEFAULT,
theme: &theme::DEFAULT,
term: Term::stderr(),
frame: 0,
height: 0,
}
}

/// Set the style of the spinner
pub fn style(mut self, style: SpinnerStyle) -> Self {
pub fn style(mut self, style: &'a SpinnerStyle) -> Self {
self.style = style;
self
}
Expand Down Expand Up @@ -91,12 +92,12 @@ impl<'a> Spinner<'a> {
fn render(&mut self) -> io::Result<String> {
let mut out = Buffer::ansi();

if self.frame > self.style.chars.len() - 1 {
if self.frame > self.style.frames.len() - 1 {
self.frame = 0
}

out.set_color(&self.theme.input_prompt)?;
write!(out, "{} ", self.style.chars[self.frame])?;
write!(out, "{} ", self.style.frames[self.frame])?;
out.reset()?;

write!(out, "{}", self.title)?;
Expand All @@ -114,66 +115,77 @@ impl<'a> Spinner<'a> {
}
}

pub(crate) static DEFAULT: Lazy<SpinnerStyle> = Lazy::new(SpinnerStyle::line);

/// The style of the spinner
///
/// # Example
/// ```rust
/// use demand::SpinnerStyle;
/// use std::time::Duration;
///
/// let style = SpinnerStyle::dots();
/// let dots_style = SpinnerStyle::dots();
/// let custom_style = SpinnerStyle {
/// frames: vec![" ", ". ", "..", "..."],
/// fps: Duration::from_millis(1000 / 10),
/// };
/// ```
pub struct SpinnerStyle {
chars: Vec<&'static str>,
fps: Duration,
/// The characters to use as frames for the spinner
pub frames: Vec<&'static str>,
/// The frames per second of the spinner
/// Usually represented as a fraction of a second in milliseconds for example `Duration::from_millis(1000/10)`
/// which would be 10 frames per second
pub fps: Duration,
}

impl SpinnerStyle {
// Create a new spinner type of dots
pub fn dots() -> Self {
Self {
chars: vec!["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"],
frames: vec!["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"],
fps: Duration::from_millis(1000 / 10),
}
}
// Create a new spinner type of jump
pub fn jump() -> Self {
Self {
chars: vec!["⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"],
frames: vec!["⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"],
fps: Duration::from_millis(1000 / 10),
}
}
// Create a new spinner type of line
pub fn line() -> Self {
Self {
chars: vec!["-", "\\", "|", "/"],
frames: vec!["-", "\\", "|", "/"],
fps: Duration::from_millis(1000 / 10),
}
}
// Create a new spinner type of points
pub fn points() -> Self {
Self {
chars: vec!["∙∙∙", "●∙∙", "∙●∙", "∙∙●"],
frames: vec!["∙∙∙", "●∙∙", "∙●∙", "∙∙●"],
fps: Duration::from_millis(1000 / 7),
}
}
// Create a new spinner type of meter
pub fn meter() -> Self {
Self {
chars: vec!["▱▱▱", "▰▱▱", "▰▰▱", "▰▰▰", "▰▰▱", "▰▱▱", "▱▱▱"],
frames: vec!["▱▱▱", "▰▱▱", "▰▰▱", "▰▰▰", "▰▰▱", "▰▱▱", "▱▱▱"],
fps: Duration::from_millis(1000 / 7),
}
}
// Create a new spinner type of mini dots
pub fn minidots() -> Self {
Self {
chars: vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
frames: vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
fps: Duration::from_millis(1000 / 12),
}
}
// Create a new spinner type of ellipsis
pub fn ellipsis() -> Self {
Self {
chars: vec![" ", ". ", ".. ", "..."],
frames: vec![" ", ". ", ".. ", "..."],
fps: Duration::from_millis(1000 / 3),
}
}
Expand All @@ -196,8 +208,8 @@ mod test {
SpinnerStyle::minidots(),
SpinnerStyle::ellipsis(),
] {
let mut spinner = Spinner::new("Loading data...").style(t);
for f in spinner.style.chars.clone().iter() {
let mut spinner = Spinner::new("Loading data...").style(&t);
for f in spinner.style.frames.clone().iter() {
assert_eq!(
format!("{} Loading data...", f),
without_ansi(spinner.render().unwrap().as_str())
Expand Down