Struct Spinner

Source
pub struct Spinner {
    pub tick_delay: Duration,
    pub message: String,
    pub style: SpinnerStyle,
    pub safe_output_terminal: SafeRawTerminal,
    pub shared_writer: SharedWriter,
    pub shutdown_sender: Sender<()>,
    /* private fields */
}

Fields§

§tick_delay: Duration§message: String§style: SpinnerStyle§safe_output_terminal: SafeRawTerminal§shared_writer: SharedWriter§shutdown_sender: Sender<()>

Implementations§

Source§

impl Spinner

Source

pub async fn try_start( spinner_message: String, tick_delay: Duration, style: SpinnerStyle, safe_output_terminal: SafeRawTerminal, shared_writer: SharedWriter, ) -> Result<Option<Spinner>>

Create a new instance of Spinner.

§Returns
  1. This will return an error if the task is already running.
  2. If the terminal is not fully interactive then it will return None, and won’t start the task. This is when the terminal is not considered fully interactive:
    • stdout is piped, eg: echo "foo" | cargo run --example spinner.
    • or all three stdin, stdout, stderr are not is_tty, eg when running in cargo test.
  3. Otherwise, it will start the task and return a Spinner instance.

More info on terminal piping:

Examples found in repository?
examples/spinner.rs (lines 73-79)
61async fn example_with_concurrent_output(style: SpinnerStyle) -> miette::Result<()> {
62    let terminal_async = TerminalAsync::try_new("$ ").await?;
63    let terminal_async = terminal_async.expect("terminal is not fully interactive");
64    let address = "127.0.0.1:8000";
65    let message_trying_to_connect = format!(
66        "This is a sample indeterminate progress message: trying to connect to server on {}",
67        &address
68    );
69
70    let mut shared_writer = terminal_async.clone_shared_writer();
71
72    // Start spinner. Automatically pauses the terminal.
73    let mut maybe_spinner = Spinner::try_start(
74        message_trying_to_connect.clone(),
75        DELAY_UNIT,
76        style,
77        Arc::new(StdMutex::new(stderr())),
78        shared_writer.clone(),
79    )
80    .await?;
81
82    // Start another task, to simulate some async work, that uses a interval to display
83    // output, for a fixed amount of time, using `terminal_async.println_prefixed()`.
84    let _ = try_join!(tokio::spawn(async move {
85        // To calculate the delay.
86        let duration = ARTIFICIAL_UI_DELAY;
87        let start = Instant::now();
88
89        // Dropping the interval cancels it.
90        let mut interval = tokio::time::interval(Duration::from_millis(DELAY_MS * 5));
91
92        loop {
93            interval.tick().await;
94            let elapsed = start.elapsed();
95            if elapsed >= duration {
96                break;
97            }
98            let _ = writeln!(shared_writer, "⏳foo");
99        }
100    }));
101
102    // Stop spinner. Automatically resumes the terminal.
103    if let Some(spinner) = maybe_spinner.as_mut() {
104        spinner
105            .stop("This is a sample final message for the spinner component: Connected to server")
106            .await?;
107    }
108
109    tokio::time::sleep(Duration::from_millis(500)).await;
110
111    Ok(())
112}
More examples
Hide additional examples
examples/terminal_async.rs (lines 386-395)
362    pub fn spawn_task_that_shows_spinner(
363        shared_writer: &mut SharedWriter,
364        readline: &mut Readline,
365        task_name: &str,
366        delay: Duration,
367    ) {
368        let mut interval = interval(delay);
369        let mut tick_counter = 0;
370        let max_tick_count = 30;
371
372        let task_name = task_name.to_string();
373
374        let shared_writer_clone_1 = shared_writer.clone();
375        let mut shared_writer_clone_2 = shared_writer.clone();
376
377        if readline.safe_spinner_is_active.lock().unwrap().is_some() {
378            _ = writeln!(
379                shared_writer,
380                "Spinner is already active, can't start another one"
381            );
382        }
383
384        tokio::spawn(async move {
385            // Try to create and start a spinner.
386            let maybe_spinner = Spinner::try_start(
387                format!(
388                    "{} - This is a sample indeterminate progress message",
389                    task_name
390                ),
391                Duration::from_millis(100),
392                SpinnerStyle::default(),
393                Arc::new(StdMutex::new(stderr())),
394                shared_writer_clone_1,
395            )
396            .await;
397
398            loop {
399                // Check for spinner shutdown (via interruption).
400                if let Ok(Some(ref spinner)) = maybe_spinner {
401                    if spinner.is_shutdown() {
402                        break;
403                    }
404                }
405
406                // Wait for the interval duration (one tick).
407                interval.tick().await;
408
409                // Don't print more than `max_tick_count` times.
410                tick_counter += 1;
411                if tick_counter >= max_tick_count {
412                    break;
413                }
414
415                // Display a message at every tick.
416                let _ = writeln!(
417                    shared_writer_clone_2,
418                    "[{task_name}] - [{tick_counter}] interval went off while spinner was spinning!"
419                );
420            }
421
422            // Don't forget to stop the spinner.
423            if let Ok(Some(mut spinner)) = maybe_spinner {
424                let msg = format!("{} - Task ended. Resuming terminal and showing any output that was generated while spinner was active.", task_name);
425                let _ = spinner.stop(msg.as_str()).await;
426            }
427        });
428    }
Source

pub fn is_shutdown(&self) -> bool

This is meant for the task that spawned this Spinner to check if it should shutdown, due to:

  1. The user pressing Ctrl-C or Ctrl-D.
  2. Or the Spinner::stop got called.
Examples found in repository?
examples/terminal_async.rs (line 401)
362    pub fn spawn_task_that_shows_spinner(
363        shared_writer: &mut SharedWriter,
364        readline: &mut Readline,
365        task_name: &str,
366        delay: Duration,
367    ) {
368        let mut interval = interval(delay);
369        let mut tick_counter = 0;
370        let max_tick_count = 30;
371
372        let task_name = task_name.to_string();
373
374        let shared_writer_clone_1 = shared_writer.clone();
375        let mut shared_writer_clone_2 = shared_writer.clone();
376
377        if readline.safe_spinner_is_active.lock().unwrap().is_some() {
378            _ = writeln!(
379                shared_writer,
380                "Spinner is already active, can't start another one"
381            );
382        }
383
384        tokio::spawn(async move {
385            // Try to create and start a spinner.
386            let maybe_spinner = Spinner::try_start(
387                format!(
388                    "{} - This is a sample indeterminate progress message",
389                    task_name
390                ),
391                Duration::from_millis(100),
392                SpinnerStyle::default(),
393                Arc::new(StdMutex::new(stderr())),
394                shared_writer_clone_1,
395            )
396            .await;
397
398            loop {
399                // Check for spinner shutdown (via interruption).
400                if let Ok(Some(ref spinner)) = maybe_spinner {
401                    if spinner.is_shutdown() {
402                        break;
403                    }
404                }
405
406                // Wait for the interval duration (one tick).
407                interval.tick().await;
408
409                // Don't print more than `max_tick_count` times.
410                tick_counter += 1;
411                if tick_counter >= max_tick_count {
412                    break;
413                }
414
415                // Display a message at every tick.
416                let _ = writeln!(
417                    shared_writer_clone_2,
418                    "[{task_name}] - [{tick_counter}] interval went off while spinner was spinning!"
419                );
420            }
421
422            // Don't forget to stop the spinner.
423            if let Ok(Some(mut spinner)) = maybe_spinner {
424                let msg = format!("{} - Task ended. Resuming terminal and showing any output that was generated while spinner was active.", task_name);
425                let _ = spinner.stop(msg.as_str()).await;
426            }
427        });
428    }
Source

pub async fn stop(&mut self, final_message: &str) -> Result<()>

Examples found in repository?
examples/spinner.rs (line 105)
61async fn example_with_concurrent_output(style: SpinnerStyle) -> miette::Result<()> {
62    let terminal_async = TerminalAsync::try_new("$ ").await?;
63    let terminal_async = terminal_async.expect("terminal is not fully interactive");
64    let address = "127.0.0.1:8000";
65    let message_trying_to_connect = format!(
66        "This is a sample indeterminate progress message: trying to connect to server on {}",
67        &address
68    );
69
70    let mut shared_writer = terminal_async.clone_shared_writer();
71
72    // Start spinner. Automatically pauses the terminal.
73    let mut maybe_spinner = Spinner::try_start(
74        message_trying_to_connect.clone(),
75        DELAY_UNIT,
76        style,
77        Arc::new(StdMutex::new(stderr())),
78        shared_writer.clone(),
79    )
80    .await?;
81
82    // Start another task, to simulate some async work, that uses a interval to display
83    // output, for a fixed amount of time, using `terminal_async.println_prefixed()`.
84    let _ = try_join!(tokio::spawn(async move {
85        // To calculate the delay.
86        let duration = ARTIFICIAL_UI_DELAY;
87        let start = Instant::now();
88
89        // Dropping the interval cancels it.
90        let mut interval = tokio::time::interval(Duration::from_millis(DELAY_MS * 5));
91
92        loop {
93            interval.tick().await;
94            let elapsed = start.elapsed();
95            if elapsed >= duration {
96                break;
97            }
98            let _ = writeln!(shared_writer, "⏳foo");
99        }
100    }));
101
102    // Stop spinner. Automatically resumes the terminal.
103    if let Some(spinner) = maybe_spinner.as_mut() {
104        spinner
105            .stop("This is a sample final message for the spinner component: Connected to server")
106            .await?;
107    }
108
109    tokio::time::sleep(Duration::from_millis(500)).await;
110
111    Ok(())
112}
More examples
Hide additional examples
examples/terminal_async.rs (line 425)
362    pub fn spawn_task_that_shows_spinner(
363        shared_writer: &mut SharedWriter,
364        readline: &mut Readline,
365        task_name: &str,
366        delay: Duration,
367    ) {
368        let mut interval = interval(delay);
369        let mut tick_counter = 0;
370        let max_tick_count = 30;
371
372        let task_name = task_name.to_string();
373
374        let shared_writer_clone_1 = shared_writer.clone();
375        let mut shared_writer_clone_2 = shared_writer.clone();
376
377        if readline.safe_spinner_is_active.lock().unwrap().is_some() {
378            _ = writeln!(
379                shared_writer,
380                "Spinner is already active, can't start another one"
381            );
382        }
383
384        tokio::spawn(async move {
385            // Try to create and start a spinner.
386            let maybe_spinner = Spinner::try_start(
387                format!(
388                    "{} - This is a sample indeterminate progress message",
389                    task_name
390                ),
391                Duration::from_millis(100),
392                SpinnerStyle::default(),
393                Arc::new(StdMutex::new(stderr())),
394                shared_writer_clone_1,
395            )
396            .await;
397
398            loop {
399                // Check for spinner shutdown (via interruption).
400                if let Ok(Some(ref spinner)) = maybe_spinner {
401                    if spinner.is_shutdown() {
402                        break;
403                    }
404                }
405
406                // Wait for the interval duration (one tick).
407                interval.tick().await;
408
409                // Don't print more than `max_tick_count` times.
410                tick_counter += 1;
411                if tick_counter >= max_tick_count {
412                    break;
413                }
414
415                // Display a message at every tick.
416                let _ = writeln!(
417                    shared_writer_clone_2,
418                    "[{task_name}] - [{tick_counter}] interval went off while spinner was spinning!"
419                );
420            }
421
422            // Don't forget to stop the spinner.
423            if let Ok(Some(mut spinner)) = maybe_spinner {
424                let msg = format!("{} - Task ended. Resuming terminal and showing any output that was generated while spinner was active.", task_name);
425                let _ = spinner.stop(msg.as_str()).await;
426            }
427        });
428    }

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<D> OwoColorize for D

Source§

fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>
where C: Color,

Set the foreground color generically Read more
Source§

fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>
where C: Color,

Set the background color generically. Read more
Source§

fn black(&self) -> FgColorDisplay<'_, Black, Self>

Change the foreground color to black
Source§

fn on_black(&self) -> BgColorDisplay<'_, Black, Self>

Change the background color to black
Source§

fn red(&self) -> FgColorDisplay<'_, Red, Self>

Change the foreground color to red
Source§

fn on_red(&self) -> BgColorDisplay<'_, Red, Self>

Change the background color to red
Source§

fn green(&self) -> FgColorDisplay<'_, Green, Self>

Change the foreground color to green
Source§

fn on_green(&self) -> BgColorDisplay<'_, Green, Self>

Change the background color to green
Source§

fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>

Change the foreground color to yellow
Source§

fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>

Change the background color to yellow
Source§

fn blue(&self) -> FgColorDisplay<'_, Blue, Self>

Change the foreground color to blue
Source§

fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>

Change the background color to blue
Source§

fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>

Change the foreground color to magenta
Source§

fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>

Change the background color to magenta
Source§

fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>

Change the foreground color to purple
Source§

fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>

Change the background color to purple
Source§

fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>

Change the foreground color to cyan
Source§

fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>

Change the background color to cyan
Source§

fn white(&self) -> FgColorDisplay<'_, White, Self>

Change the foreground color to white
Source§

fn on_white(&self) -> BgColorDisplay<'_, White, Self>

Change the background color to white
Source§

fn default_color(&self) -> FgColorDisplay<'_, Default, Self>

Change the foreground color to the terminal default
Source§

fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>

Change the background color to the terminal default
Source§

fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>

Change the foreground color to bright black
Source§

fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>

Change the background color to bright black
Source§

fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>

Change the foreground color to bright red
Source§

fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>

Change the background color to bright red
Source§

fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>

Change the foreground color to bright green
Source§

fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>

Change the background color to bright green
Source§

fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>

Change the foreground color to bright yellow
Source§

fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>

Change the background color to bright yellow
Source§

fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>

Change the foreground color to bright blue
Source§

fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>

Change the background color to bright blue
Source§

fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>

Change the foreground color to bright magenta
Source§

fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>

Change the background color to bright magenta
Source§

fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>

Change the foreground color to bright purple
Source§

fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>

Change the background color to bright purple
Source§

fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>

Change the foreground color to bright cyan
Source§

fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>

Change the background color to bright cyan
Source§

fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>

Change the foreground color to bright white
Source§

fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>

Change the background color to bright white
Source§

fn bold(&self) -> BoldDisplay<'_, Self>

Make the text bold
Source§

fn dimmed(&self) -> DimDisplay<'_, Self>

Make the text dim
Source§

fn italic(&self) -> ItalicDisplay<'_, Self>

Make the text italicized
Source§

fn underline(&self) -> UnderlineDisplay<'_, Self>

Make the text underlined
Make the text blink
Make the text blink (but fast!)
Source§

fn reversed(&self) -> ReversedDisplay<'_, Self>

Swap the foreground and background colors
Source§

fn hidden(&self) -> HiddenDisplay<'_, Self>

Hide the text
Source§

fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>

Cross out the text
Source§

fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>
where Color: DynColor,

Set the foreground color at runtime. Only use if you do not know which color will be used at compile-time. If the color is constant, use either OwoColorize::fg or a color-specific method, such as OwoColorize::green, Read more
Source§

fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>
where Color: DynColor,

Set the background color at runtime. Only use if you do not know what color to use at compile-time. If the color is constant, use either OwoColorize::bg or a color-specific method, such as OwoColorize::on_yellow, Read more
Source§

fn fg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>

Set the foreground color to a specific RGB value.
Source§

fn bg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>

Set the background color to a specific RGB value.
Source§

fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>

Sets the foreground color to an RGB value.
Source§

fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>

Sets the background color to an RGB value.
Source§

fn style(&self, style: Style) -> Styled<&Self>

Apply a runtime-determined style
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more