Module conciliator::spin

source ·
Expand description

Spinner animations

A couple of different Animations are provided, but you can also define your own using Animation::new or by implementing Animate.
Check out the example binary to see the provided Animations in action!


Because the animation requires terminal escape codes to work it can’t always be displayed. To keep it simple, the Spinner reuses the determination that the Stream made on whether to emit color escape codes to decide whether to display the animation. This means there is no way (for the user) to disable color output without also disabling the animation (and vice versa).

Obviously, the Spinner takes care of this for you: it’s not an error to use any of it’s methods when the animation isn’t being displayed.

§Output Stream

The Spinner animation is printed to the stderr output stream instead of stdout. This makes no difference in normal operation since the terminal emulator will merge the two back together, but it means that the user can still see the animation when they redirect (“pipe”) the standard output into another program (i.e. with | ) or into a file (>>). Of course, it’s also possible (though far less common) to redirect the stderr stream (with 2>>, for example). In this case, the animation (probably) won’t be rendered for the user, so no escape codes are emitted. In fact, not even the Spinner Messages will be printed, only output from using the Conciliator methods will be written (to stdout, as normal).

§Hidden Cursor

While the Spinner is active, the cursor is hidden. Since it would jump around or cover parts of the animation, it looks better that way. Unfortunately, the terminal will not automatically un-hide the cursor when the program ends, which means the cursor might stay hidden when your program crashes, or is killed / interrupted (i.e. Ctrl+C) while spinning. You can mitigate this by implementing orderly shutdown and ensuring that destructors get run as the program ends. Of course, there’s nothing your program can do if it gets SIGKILL’d, but if you avoid std::process::exit and install a signal handler for SIGINT, you probably have most situations covered. Even if your program panics, the Rust panic handler will (try to) run every destructor before exiting, and the escape code for showing the cursor will get printed by one of them.
If all else fails, you can usually still fix your terminal manually using the reset command.

§Output while spinning

The animation is printed to the terminal using control characters (and \r) to stay in the same line and always overwrite the last frame of the animation. Because of this, access to the output needs to be synchronized: whenever there’s something to be printed, the line with the animation needs to be erased first, then the thing is printed, and then the animation put back. The Spinner takes care of this, but it can only do that when you go through its Conciliator methods – instead of the Claw’s. So, to prevent this from happening accidentally, the Spinner holds a mutable reference to the Claw. Of course, you can still mess it up by going to the standard output directly, i.e. using std::io::Stdout, println, dbg, eprint and so on. You could create multiple Claws, too. Don’t.

§Async

By default, creating a Spinner will spawn a new thread to do the animation. Then, when it is dropped, finished, or cleared, the “main” thread will block until the other thread returns. While these blocks should resolve very quickly, it could still be an issue when used within a runtime / async executor, because while a thread is blocking, the runtime can’t schedule something else on it.

Because of this, there is another implementation of the Spinner, specifically for use with the Tokio runtime. It spawns a Tokio task instead of a thread, and it uses Tokio message queues instead of std::sync::mpsc. To use this implementation instead, enable the tokio crate feature.

When you enable the tokio feature, Spinner::clear and Spinner::finish will become async, and you need to make sure to call one of them, as explained below.

§Dropping the Spinner

Only the synchronous Spinner should ever be dropped implicitly (that is, without calling Spinner::clear or Spinner::finish).

This means: if you enable the tokio feature (which is off by default), you have to make sure to call and await Spinner::clear or Spinner::finish when you’re done with it! If you don’t explicitly drop it using one of these methods, the destructor for it has to clean it up, and it may do a worse job. Because destructors cannot be async, the task will be aborted instead of joined, which should still work, but it could cause one or two lines of output being mangled.

Again, this is only an issue if you drop the Spinner without calling either Spinner::clear or Spinner::finish, and the tokio feature is enabled. For the default, thread-based implementation, dropping the Spinner is exactly the same as calling Spinner::clear explicitly.

§Example

Creating a Spinner with the DOT Animation (taken from FGRibreau/spinners).

use conciliator::{Conciliator, Spinner};
use conciliator::spin::DOT;
// `mut` so that the Spinner can get an exclusive (mutable) reference
let mut con = conciliator::init();
con.status("Starting process");
let sp = Spinner::new(&mut con, DOT, "Downloading thing...");
// < download the thing >
sp.status("Download complete");
sp.message("Unpacking thing...");
// < unpack the thing >
sp.status("Thing unpacked");
sp.message("Process finished");
sp.finish().await; // without the tokio feature, this is not async
// you can only use the Claw after the Spinner is finished
con.info("Doing other thing");

Structs§

Constants§

  • 3-wide Braille character animation, two opposing groups of dots looping
  • Narrow spinner animation using Braille characters
  • Spinner animation using Braille characters, wider version of DOT
  • Same as LOOP, but even wider, Braille dots go all the way to the edge
  • Narrow spinner animation using half-filled square characters
  • Narrow spinner animation using triangle characters

Traits§