commit 78ed43eb57436c8363fd83fa7d642e893fa056bd Author: ad4mx Date: Fri Jul 15 19:59:22 2022 +0200 Initial commit diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..e8d486a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "cargo" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..d1140bd --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,29 @@ +name: Rust + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Lint + run: cargo clippy + - name: Format + run: cargo fmt + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + - name: Run examples + run: cargo run --example simple + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4a9b0c6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "spinoff" +version = "0.1.0" +edition = "2021" +author = "ad4m" +description = "๐Ÿ”จ Simple to use Rust library for displaying spinners in the terminal." +license = "MIT" +homepage = "https://github.com/ad4mx/spinoff" +repository = "https://github.com/ad4mx/spinoff" +readme = "README.md" +keywords = ["spinner", "spin", "loader", "term", "terminal"] +categories = ["command-line-interface"] +include = ["src/**/*", "README.md"] + +[dependencies] +lazy_static = "1.4.0" +maplit = "1.0.2" +yansi = "0.5.1" +strum = { version = "0.24.0", features = ["derive"] } + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e3702e0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 ad4m + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..06b3b7d --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# spinoff +`spinoff` is a simple to use library for displaying spinners in the terminal, with plenty of features and options. + +[![Cargo version](https://img.shields.io/crates/v/spinoff.svg)](https://crates.io/crates/spinoff) [![Crates.io](https://img.shields.io/crates/l/spinoff.svg)] [![Crates.io](https://img.shields.io/crates/d/spinoff.svg)](https://crates.io/crates/spinoff) + +## Usage + +```rust +use spinoff::Spinners; +use std::thread::sleep; +use std::time::Duration; + +let spinner = spinoff::new(Spinners::Dots, "Loading...", "Blue".into()); // Can also be Some("blue") or None +sleep(Duration::from_secs(3)); +spinner.success("Done!"); +``` + +## Documentation + +* All documentation can be found on the [Docs.rs page](https://docs.rs/spinoff/latest/spinoff/). +* If you want to see all the available `Spinner` options, check the [`Spinners`](src/utils/spinner_enum.rs) enum. + +## Examples + +To run some of the included examples, use: +```bash +cargo run --example all_spinners +``` + +```bash +cargo run --example simple +``` + +## Contributing + +Any contributions to this crate are highly appreciated. If you have any ideas/suggestions/bug fixes, please open an issue or pull request. + +## License + +This crate is licensed under the [MIT license](LICENSE). \ No newline at end of file diff --git a/examples/all_spinners.rs b/examples/all_spinners.rs new file mode 100644 index 0000000..8dd1ec4 --- /dev/null +++ b/examples/all_spinners.rs @@ -0,0 +1,12 @@ +use spinoff::Spinners; +use std::thread::sleep; +use std::time::Duration; +use strum::IntoEnumIterator; +fn main() { + for spinner in Spinners::iter() { + let spin = spinoff::new(spinner, "", None); + sleep(Duration::from_secs(1)); + spin.clear(); + } + println!("Done!"); +} diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 0000000..ca7e39c --- /dev/null +++ b/examples/simple.rs @@ -0,0 +1,8 @@ +use spinoff::Spinners; +use std::thread::sleep; +use std::time::Duration; +fn main() { + let sp = spinoff::new(Spinners::Dots, "Loading...", None); + sleep(Duration::from_millis(800)); + sp.success("Success!"); +} diff --git a/examples/stop_and_persist.rs b/examples/stop_and_persist.rs new file mode 100644 index 0000000..41d5169 --- /dev/null +++ b/examples/stop_and_persist.rs @@ -0,0 +1,9 @@ +use spinoff::Spinners; +use std::thread::sleep; +use std::time::Duration; + +fn main() { + let sp = spinoff::new(Spinners::Dots, "Loading...", None); + sleep(Duration::from_secs(5)); + sp.stop_and_persist("๐Ÿ•", "Pizza!", "yellow".into()); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..2ac7e39 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,281 @@ +//! ## spinoff +//! +//! `spinoff` is a simple library for displaying spinners in the terminal. +//! ### Usage +//! +//! ```rust +//! use spinoff::Spinners; +//! use std::thread::sleep; +//! use std::time::Duration; +//! +//! let sp = spinoff::new(Spinners::Dots, "Loading...", None); +//! sleep(Duration::from_millis(800)); +//! sp.success("Success!"); +//! +//! ``` +//! ### Spinners +//! There are over 80+ spinners available in the `Spinners` enum. +//! +//! ### Colors +//! You can also color your spinners without any hassle. Simply pass a color to the `color` option. +//! There are 6 colors available: `blue`, `green`, `red`, `yellow`, `cyan`, `white`. +//! Don't want any of that? Simply pass `None` to the `color` option. +//! +#![allow(clippy::needless_return)] +mod utils; +use crate::utils::printer::{delete_last_line, init_color}; +use crate::utils::spinner_data::SPINNER_FRAMES; +pub use crate::utils::spinner_enum::Spinners; +use std::io::{stdout, Write}; +use std::sync::atomic::AtomicBool; +use std::sync::Arc; +use std::thread; +// Using this type for better readability. +type StringLiteral = &'static str; + +/// All methods for the spinner are implemented in this struct. +/// This struct has an `Arc` field, which is later used in the `stop` type methods to stop the thread printing the spinner. +/// The `msg` field is needed for the `clear` and `stop` methods for knowing how many spaces to print to clean up the previous spinner. + +pub struct Spinner { + still_spinning: Arc, + msg: StringLiteral, +} +/// Create a new spinner. +/// +/// # Arguments +/// * `spinner_type` - The spinner to use. +/// * `msg` - The message to display. +/// * `color` - The color of the spinner. +/// +/// # Example +/// ```rust +/// use spinoff::Spinners; +/// use std::thread::sleep; +/// use std::time::Duration; +/// +/// let sp = spinoff::new(Spinners::Dots, "Hello", "blue".into()); // Color can also be `None` or `Some("blue")` +/// sleep(Duration::from_millis(800)); +/// sp.clear(); +/// ``` +/// # Panics +/// * `Invalid color` - If the color is not one of the following: `blue`, `green`, `red`, `yellow`, `cyan`, `white` or `None`. +/// * `Invalid spinner` - If the spinner is not one of those belonging to the `Spinners` enum. +/// +/// # Notes +/// * The spinner immediately starts spinning upon creation. +pub fn new(spinner_type: Spinners, msg: StringLiteral, color: Option) -> Spinner { + let spinner_data = SPINNER_FRAMES + .get(&spinner_type) + .expect("invalid spinner: expected variant of Spinners enum"); + let mut stdout = stdout(); + let mut i = 0; + let still_spinning = Arc::new(AtomicBool::new(true)); + // Clone the atomic bool so that we can use it in the thread and return the original one later. + let still_spinning_cloned = still_spinning.clone(); + // We use atomic bools to make the thread stop itself when the `spinner.stop()` method is called. + thread::spawn(move || { + while still_spinning_cloned.load(std::sync::atomic::Ordering::Relaxed) { + let text = format!( + "\r{} {}", + init_color(color, spinner_data.frames[i].to_string()), + msg + ); + stdout.write_all(text.as_bytes()).unwrap(); + stdout.flush().unwrap(); + thread::sleep(std::time::Duration::from_millis( + spinner_data.interval as u64, + )); + i = (i + 1) % spinner_data.frames.len(); + } + }); + // Return a Spinner struct so we can implement methods on it instead of `spinoff::stop()` etc. + let spinner = Spinner { + still_spinning, + msg, + }; + return spinner; +} + +impl Spinner { + /// Stop the spinner. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp.stop(); + /// ``` + /// # Notes + /// * The spinner will be deleted after this method is called, the message will remain though. + /// * This method also sets the `still_spinning` atomic bool to `false`, which stops the spinner thread. + /// * This method cannot be called if the spinner is already stopped. + /// + pub fn stop(self) { + self.still_spinning + .store(false, std::sync::atomic::Ordering::Relaxed); + print!("\r"); + println!("{} ", self.msg); + } + + /// Stops the spinner and prints a message on a new line. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp.stop_with_message("Bye", "red".into()); + /// ``` + /// + /// # Panics + /// * The method will panic if the color is not one of the following: `blue`, `green`, `red`, `yellow`, `cyan`, `white` or `None`. + /// + /// # Notes + /// * This method cannot be called if the spinner is already stopped. + /// + pub fn stop_with_message(self, msg: StringLiteral, color: Option) { + self.stop(); + println!("{}", init_color(color, msg.into())); + } + + /// Deletes the spinner and message and prints a new line with a symbol and message. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp.stop_and_persist("๐Ÿ•", "Pizza!", None); + /// ``` + /// + /// # Panics + /// * The method will panic if the color is not one of the following: `blue`, `green`, `red`, `yellow`, `cyan`, `white` or `None`. + /// + /// # Notes + /// * This method will delete the last line of the terminal, so it is recommended to not print anything in between the spinner and the success message. + /// * This method cannot be called if the spinner is already stopped. + pub fn stop_and_persist(self, symbol: StringLiteral, msg: StringLiteral, color: Option) { + self.clear(); + println!("{} {}", init_color(color, symbol.into()), &msg); + } + + /// Deletes the last line of the terminal and prints a success symbol with a message. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp.success("Success!"); + /// ``` + /// + /// # Notes + /// * This method cannot be called if the spinner is already stopped. + /// * This method will delete the last line of the terminal, so it is recommended to not print anything in between the spinner and the success message. + pub fn success(self, msg: StringLiteral) { + self.stop_and_persist("โœ”", msg, "green".into()); + } + + /// Deletes the last line of the terminal and prints a failure symbol with a message. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp.fail("Failed!"); + /// ``` + /// + /// # Notes + /// * This method will delete the last line of the terminal, so it is recommended to not print anything in between the spinner and the failure message. + /// * This method cannot be called if the spinner is already stopped. + /// + pub fn fail(self, msg: StringLiteral) { + self.stop_and_persist("โœ–", msg, "red".into()); + } + + /// Deletes the last line of the terminal and prints a warning symbol with a message. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp.warn("Look out!"); + /// ``` + /// + /// + /// # Notes + /// * This method will delete the last line of the terminal, so it is recommended to not print anything in between the spinner and the warning message. + /// * This method cannot be called if the spinner is already stopped. + pub fn warn(self, msg: StringLiteral) { + self.stop_and_persist("โš  ", msg, "yellow".into()); + } + + /// Deletes the last line of the terminal and prints a new spinner. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let mut sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp = sp.update(Spinners::Dots2, "Goodbye".into(), None); + /// sleep(Duration::from_millis(800)); + /// sp.stop(); + /// ``` + /// + /// # Notes + /// * This method will delete the last line of the terminal, so it is recommended to not print anything in between the spinner and the new spinner instance. + /// * This method cannot be called if the spinner is already stopped. + /// + pub fn update(self, spinner: Spinners, msg: StringLiteral, color: Option) -> Spinner { + self.still_spinning + .store(false, std::sync::atomic::Ordering::Relaxed); + delete_last_line(self.msg); + self::new(spinner, msg, color) + } + /// Deletes the last line of the terminal. + /// + /// # Example + /// ```rust + /// use spinoff::Spinners; + /// use std::thread::sleep; + /// use std::time::Duration; + /// + /// let mut sp = spinoff::new(Spinners::Dots, "Hello", None); + /// sleep(Duration::from_millis(800)); + /// sp.clear(); + /// ``` + /// + /// # Notes + /// * This method will delete the last line of the terminal, so it is recommended to not print anything in between the spinner and the `delete` method call. + /// * This method cannot be called if the spinner is already stopped. + pub fn clear(self) { + self.still_spinning + .store(false, std::sync::atomic::Ordering::Relaxed); + delete_last_line(self.msg); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..02e2365 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,3 @@ +pub mod printer; +pub mod spinner_data; +pub mod spinner_enum; diff --git a/src/utils/printer.rs b/src/utils/printer.rs new file mode 100644 index 0000000..b05e5bf --- /dev/null +++ b/src/utils/printer.rs @@ -0,0 +1,25 @@ +use yansi::Paint; + +use crate::StringLiteral; + +pub fn delete_last_line(msg: StringLiteral) { + print!("\r"); + for _ in 0..(msg.len() + 30) { + print!(" "); + } + print!("\r"); +} + +/// Accepts a color option and spinner, returns a Paint object (e.g. `Paint::green(spinner)`) +pub fn init_color(color: Option<&str>, spinner: String) -> Paint { + match color { + Some("blue") => Paint::blue(spinner), + Some("green") => Paint::green(spinner), + Some("red") => Paint::red(spinner), + Some("yellow") => Paint::yellow(spinner), + Some("cyan") => Paint::cyan(spinner), + Some("white") => Paint::new(spinner), + None => Paint::new(spinner), + _ => panic!("invalid color: expected one of the following: blue, green, red, yellow, cyan, white or None"), + } +} diff --git a/src/utils/spinner_data.rs b/src/utils/spinner_data.rs new file mode 100644 index 0000000..1609d79 --- /dev/null +++ b/src/utils/spinner_data.rs @@ -0,0 +1,1190 @@ +use crate::{utils::spinner_enum::Spinners, StringLiteral}; +use lazy_static::lazy_static; +use maplit::{self, hashmap}; +use std::collections::HashMap; +/// A Struct that contains the data for a spinner. +/// Frames is a Vec of &str, each &str is a frame of the spinner. +/// Interval is the number of milliseconds to wait before moving to the next frame. +pub struct SpinnerFrames { + pub frames: Vec, + pub interval: u16, +} + +lazy_static! { + /// A HashMap of all the spinners from the Spinners enum and their data. + /// The reason for using a HashMap instead of something like a JSON file is + /// because there's no need for extra files and/or crates for parsing data. + pub static ref SPINNER_FRAMES: HashMap = { + hashmap! { + Spinners::Dots => SpinnerFrames {frames: vec![ + "โ ‹", + "โ ™", + "โ น", + "โ ธ", + "โ ผ", + "โ ด", + "โ ฆ", + "โ ง", + "โ ‡", + "โ " + ], interval: 80}, + Spinners::Dots2 => SpinnerFrames {frames: vec![ + "โ ‹", + "โ ™", + "โ š", + "โ ž", + "โ –", + "โ ฆ", + "โ ด", + "โ ฒ", + "โ ณ", + "โ “" + ], interval: 80}, + Spinners::Dots3 => SpinnerFrames {frames: vec![ + "โ ‹", + "โ ™", + "โ š", + "โ ž", + "โ –", + "โ ฆ", + "โ ด", + "โ ฒ", + "โ ณ", + "โ “" + ], interval: 80}, + Spinners::Dots4 => SpinnerFrames {frames: vec![ + "โ „", + "โ †", + "โ ‡", + "โ ‹", + "โ ™", + "โ ธ", + "โ ฐ", + "โ  ", + "โ ฐ", + "โ ธ", + "โ ™", + "โ ‹", + "โ ‡", + "โ †" + ], interval: 80}, + Spinners::Dots5 => SpinnerFrames {frames: vec![ + "โ ‹", + "โ ™", + "โ š", + "โ ’", + "โ ‚", + "โ ‚", + "โ ’", + "โ ฒ", + "โ ด", + "โ ฆ", + "โ –", + "โ ’", + "โ ", + "โ ", + "โ ’", + "โ “", + "โ ‹" + ], interval: 80}, + Spinners::Dots6 => SpinnerFrames {frames: vec![ + "โ ", + "โ ‰", + "โ ™", + "โ š", + "โ ’", + "โ ‚", + "โ ‚", + "โ ’", + "โ ฒ", + "โ ด", + "โ ค", + "โ „", + "โ „", + "โ ค", + "โ ด", + "โ ฒ", + "โ ’", + "โ ‚", + "โ ‚", + "โ ’", + "โ š", + "โ ™", + "โ ‰", + "โ " + ], interval: 80}, + Spinners::Dots7 => SpinnerFrames {frames: vec![ + "โ ˆ", + "โ ‰", + "โ ‹", + "โ “", + "โ ’", + "โ ", + "โ ", + "โ ’", + "โ –", + "โ ฆ", + "โ ค", + "โ  ", + "โ  ", + "โ ค", + "โ ฆ", + "โ –", + "โ ’", + "โ ", + "โ ", + "โ ’", + "โ “", + "โ ‹", + "โ ‰", + "โ ˆ" + ], interval: 80}, + Spinners::Dots8 => SpinnerFrames {frames: vec![ + "โ ", + "โ ", + "โ ‰", + "โ ™", + "โ š", + "โ ’", + "โ ‚", + "โ ‚", + "โ ’", + "โ ฒ", + "โ ด", + "โ ค", + "โ „", + "โ „", + "โ ค", + "โ  ", + "โ  ", + "โ ค", + "โ ฆ", + "โ –", + "โ ’", + "โ ", + "โ ", + "โ ’", + "โ “", + "โ ‹", + "โ ‰", + "โ ˆ", + "โ ˆ" + ], interval: 80}, + Spinners::Dots9 => SpinnerFrames {frames: vec![ + "โขน", + "โขบ", + "โขผ", + "โฃธ", + "โฃ‡", + "โกง", + "โก—", + "โก" + ], interval: 80}, + Spinners::Dots10 => SpinnerFrames {frames: vec![ + "โข„", + "โข‚", + "โข", + "โก", + "โกˆ", + "โก", + "โก " + ], interval: 80}, + Spinners::Dots11 => SpinnerFrames {frames: vec![ + "โ ", + "โ ‚", + "โ „", + "โก€", + "โข€", + "โ  ", + "โ ", + "โ ˆ" + ], interval: 100}, + Spinners::Dots12 => SpinnerFrames {frames: vec![ + "โข€โ €", + "โก€โ €", + "โ „โ €", + "โข‚โ €", + "โก‚โ €", + "โ …โ €", + "โขƒโ €", + "โกƒโ €", + "โ โ €", + "โข‹โ €", + "โก‹โ €", + "โ โ ", + "โข‹โ ", + "โก‹โ ", + "โ โ ‰", + "โ ‹โ ‰", + "โ ‹โ ‰", + "โ ‰โ ™", + "โ ‰โ ™", + "โ ‰โ ฉ", + "โ ˆโข™", + "โ ˆโก™", + "โขˆโ ฉ", + "โก€โข™", + "โ „โก™", + "โข‚โ ฉ", + "โก‚โข˜", + "โ …โก˜", + "โขƒโ จ", + "โกƒโข", + "โ โก", + "โข‹โ  ", + "โก‹โข€", + "โ โก", + "โข‹โ ", + "โก‹โ ", + "โ โ ‰", + "โ ‹โ ‰", + "โ ‹โ ‰", + "โ ‰โ ™", + "โ ‰โ ™", + "โ ‰โ ฉ", + "โ ˆโข™", + "โ ˆโก™", + "โ ˆโ ฉ", + "โ €โข™", + "โ €โก™", + "โ €โ ฉ", + "โ €โข˜", + "โ €โก˜", + "โ €โ จ", + "โ €โข", + "โ €โก", + "โ €โ  ", + "โ €โข€", + "โ €โก€" + ], interval: 80}, + Spinners::Dots8Bit => SpinnerFrames {frames: vec![ + "โ €", + "โ ", + "โ ‚", + "โ ƒ", + "โ „", + "โ …", + "โ †", + "โ ‡", + "โก€", + "โก", + "โก‚", + "โกƒ", + "โก„", + "โก…", + "โก†", + "โก‡", + "โ ˆ", + "โ ‰", + "โ Š", + "โ ‹", + "โ Œ", + "โ ", + "โ Ž", + "โ ", + "โกˆ", + "โก‰", + "โกŠ", + "โก‹", + "โกŒ", + "โก", + "โกŽ", + "โก", + "โ ", + "โ ‘", + "โ ’", + "โ “", + "โ ”", + "โ •", + "โ –", + "โ —", + "โก", + "โก‘", + "โก’", + "โก“", + "โก”", + "โก•", + "โก–", + "โก—", + "โ ˜", + "โ ™", + "โ š", + "โ ›", + "โ œ", + "โ ", + "โ ž", + "โ Ÿ", + "โก˜", + "โก™", + "โกš", + "โก›", + "โกœ", + "โก", + "โกž", + "โกŸ", + "โ  ", + "โ ก", + "โ ข", + "โ ฃ", + "โ ค", + "โ ฅ", + "โ ฆ", + "โ ง", + "โก ", + "โกก", + "โกข", + "โกฃ", + "โกค", + "โกฅ", + "โกฆ", + "โกง", + "โ จ", + "โ ฉ", + "โ ช", + "โ ซ", + "โ ฌ", + "โ ญ", + "โ ฎ", + "โ ฏ", + "โกจ", + "โกฉ", + "โกช", + "โกซ", + "โกฌ", + "โกญ", + "โกฎ", + "โกฏ", + "โ ฐ", + "โ ฑ", + "โ ฒ", + "โ ณ", + "โ ด", + "โ ต", + "โ ถ", + "โ ท", + "โกฐ", + "โกฑ", + "โกฒ", + "โกณ", + "โกด", + "โกต", + "โกถ", + "โกท", + "โ ธ", + "โ น", + "โ บ", + "โ ป", + "โ ผ", + "โ ฝ", + "โ พ", + "โ ฟ", + "โกธ", + "โกน", + "โกบ", + "โกป", + "โกผ", + "โกฝ", + "โกพ", + "โกฟ", + "โข€", + "โข", + "โข‚", + "โขƒ", + "โข„", + "โข…", + "โข†", + "โข‡", + "โฃ€", + "โฃ", + "โฃ‚", + "โฃƒ", + "โฃ„", + "โฃ…", + "โฃ†", + "โฃ‡", + "โขˆ", + "โข‰", + "โขŠ", + "โข‹", + "โขŒ", + "โข", + "โขŽ", + "โข", + "โฃˆ", + "โฃ‰", + "โฃŠ", + "โฃ‹", + "โฃŒ", + "โฃ", + "โฃŽ", + "โฃ", + "โข", + "โข‘", + "โข’", + "โข“", + "โข”", + "โข•", + "โข–", + "โข—", + "โฃ", + "โฃ‘", + "โฃ’", + "โฃ“", + "โฃ”", + "โฃ•", + "โฃ–", + "โฃ—", + "โข˜", + "โข™", + "โขš", + "โข›", + "โขœ", + "โข", + "โขž", + "โขŸ", + "โฃ˜", + "โฃ™", + "โฃš", + "โฃ›", + "โฃœ", + "โฃ", + "โฃž", + "โฃŸ", + "โข ", + "โขก", + "โขข", + "โขฃ", + "โขค", + "โขฅ", + "โขฆ", + "โขง", + "โฃ ", + "โฃก", + "โฃข", + "โฃฃ", + "โฃค", + "โฃฅ", + "โฃฆ", + "โฃง", + "โขจ", + "โขฉ", + "โขช", + "โขซ", + "โขฌ", + "โขญ", + "โขฎ", + "โขฏ", + "โฃจ", + "โฃฉ", + "โฃช", + "โฃซ", + "โฃฌ", + "โฃญ", + "โฃฎ", + "โฃฏ", + "โขฐ", + "โขฑ", + "โขฒ", + "โขณ", + "โขด", + "โขต", + "โขถ", + "โขท", + "โฃฐ", + "โฃฑ", + "โฃฒ", + "โฃณ", + "โฃด", + "โฃต", + "โฃถ", + "โฃท", + "โขธ", + "โขน", + "โขบ", + "โขป", + "โขผ", + "โขฝ", + "โขพ", + "โขฟ", + "โฃธ", + "โฃน", + "โฃบ", + "โฃป", + "โฃผ", + "โฃฝ", + "โฃพ", + "โฃฟ" + ], interval: 80}, + Spinners::Line => SpinnerFrames {frames: vec![ + "-", + "\\", + "|", + "/" + ], interval: 130}, + Spinners::Line2 => SpinnerFrames {frames: vec![ + "โ ‚", + "-", + "โ€“", + "โ€”", + "โ€“", + "-" + ], interval: 100}, + Spinners::Pipe => SpinnerFrames {frames: vec![ + "โ”ค", + "โ”˜", + "โ”ด", + "โ””", + "โ”œ", + "โ”Œ", + "โ”ฌ", + "โ”" + ], interval: 100}, + Spinners::SimpleDots => SpinnerFrames {frames: vec![ + ". ", + ".. ", + "...", + " " + ], interval: 400}, + Spinners::SimpleDotsScrolling => SpinnerFrames {frames: vec![ + ". ", + ".. ", + "...", + " ..", + " .", + " " + ], interval: 200}, + Spinners::Star => SpinnerFrames {frames: vec![ + "โœถ", + "โœธ", + "โœน", + "โœบ", + "โœน", + "โœท" + ], interval: 70}, + Spinners::Star2 => SpinnerFrames {frames: vec![ + "+", + "x", + "*" + ], interval: 80}, + Spinners::Flip => SpinnerFrames {frames: vec![ + "_", + "_", + "_", + "-", + "`", + "`", + "'", + "ยด", + "-", + "_", + "_", + "_" + ], interval: 70}, + Spinners::Hamburger => SpinnerFrames {frames: vec![ + "โ˜ฑ", + "โ˜ฒ", + "โ˜ด" + ], interval: 100}, + Spinners::GrowVertical => SpinnerFrames {frames: vec![ + "โ–", + "โ–ƒ", + "โ–„", + "โ–…", + "โ–†", + "โ–‡", + "โ–†", + "โ–…", + "โ–„", + "โ–ƒ" + ], interval: 120}, + Spinners::GrowHorizontal => SpinnerFrames {frames: vec![ + "โ–", + "โ–Ž", + "โ–", + "โ–Œ", + "โ–‹", + "โ–Š", + "โ–‰", + "โ–Š", + "โ–‹", + "โ–Œ", + "โ–", + "โ–Ž" + ], interval: 120}, + Spinners::Balloon => SpinnerFrames {frames: vec![ + " ", + ".", + "o", + "O", + "@", + "*", + " " + ], interval: 140}, + Spinners::Balloon2 => SpinnerFrames {frames: vec![ + ".", + "o", + "O", + "ยฐ", + "O", + "o", + "." + ], interval: 120}, + Spinners::Noise => SpinnerFrames {frames: vec![ + "โ–“", + "โ–’", + "โ–‘" + ], interval: 100}, + Spinners::Bounce => SpinnerFrames {frames: vec![ + "โ ", + "โ ‚", + "โ „", + "โ ‚" + ], interval: 120}, + Spinners::BoxBounce => SpinnerFrames {frames: vec![ + "โ––", + "โ–˜", + "โ–", + "โ–—" + ], interval: 120}, + Spinners::BoxBounce2 => SpinnerFrames {frames: vec![ + "โ–Œ", + "โ–€", + "โ–", + "โ–„" + ], interval: 100}, + Spinners::Triangle => SpinnerFrames {frames: vec![ + "โ—ข", + "โ—ฃ", + "โ—ค", + "โ—ฅ" + ], interval: 50}, + Spinners::Arc => SpinnerFrames {frames: vec![ + "โ—œ", + "โ— ", + "โ—", + "โ—ž", + "โ—ก", + "โ—Ÿ" + ], interval: 100}, + Spinners::Circle => SpinnerFrames {frames: vec![ + "โ—ก", + "โŠ™", + "โ— " + ], interval: 120}, + Spinners::SquareCorners => SpinnerFrames {frames: vec![ + "โ—ฐ", + "โ—ณ", + "โ—ฒ", + "โ—ฑ" + ], interval: 180}, + Spinners::CircleQuarters => SpinnerFrames {frames: vec![ + "โ—ด", + "โ—ท", + "โ—ถ", + "โ—ต" + ], interval: 120}, + Spinners::CircleHalves => SpinnerFrames {frames: vec![ + "โ—", + "โ—“", + "โ—‘", + "โ—’" + ], interval: 50}, + Spinners::Squish => SpinnerFrames {frames: vec![ + "โ•ซ", + "โ•ช" + ], interval: 100}, + Spinners::Toggle => SpinnerFrames {frames: vec![ + "โŠถ", + "โŠท" + ], interval: 250}, + Spinners::Toggle2 => SpinnerFrames {frames: vec![ + "โ–ซ", + "โ–ช" + ], interval: 80}, + Spinners::Toggle3 => SpinnerFrames {frames: vec![ + "โ–ก", + "โ– " + ], interval: 120}, + Spinners::Toggle4 => SpinnerFrames {frames: vec![ + "โ– ", + "โ–ก", + "โ–ช", + "โ–ซ" + ], interval: 100}, + Spinners::Toggle5 => SpinnerFrames {frames: vec![ + "โ–ฎ", + "โ–ฏ" + ], interval: 100}, + Spinners::Toggle6 => SpinnerFrames {frames: vec![ + "แ€", + "แ€" + ], interval: 300}, + Spinners::Toggle7 => SpinnerFrames {frames: vec![ + "โฆพ", + "โฆฟ" + ], interval: 80}, + Spinners::Toggle8 => SpinnerFrames {frames: vec![ + "โ—", + "โ—Œ" + ], interval: 100}, + Spinners::Toggle9 => SpinnerFrames {frames: vec![ + "โ—‰", + "โ—Ž" + ], interval: 100}, + Spinners::Toggle10 => SpinnerFrames {frames: vec![ + "ใŠ‚", + "ใŠ€", + "ใŠ" + ], interval: 100}, + Spinners::Toggle11 => SpinnerFrames {frames: vec![ + "โง‡", + "โง†" + ], interval: 50}, + Spinners::Toggle12 => SpinnerFrames {frames: vec![ + "โ˜—", + "โ˜–" + ], interval: 120}, + Spinners::Toggle13 => SpinnerFrames {frames: vec![ + "=", + "*", + "-" + ], interval: 80}, + Spinners::Arrow => SpinnerFrames {frames: vec![ + "โ†", + "โ†–", + "โ†‘", + "โ†—", + "โ†’", + "โ†˜", + "โ†“", + "โ†™" + ], interval: 100}, + Spinners::Arrow2 => SpinnerFrames {frames: vec![ + "โฌ†๏ธ ", + "โ†—๏ธ ", + "โžก๏ธ ", + "โ†˜๏ธ ", + "โฌ‡๏ธ ", + "โ†™๏ธ ", + "โฌ…๏ธ ", + "โ†–๏ธ " + ], interval: 80}, + Spinners::Arrow3 => SpinnerFrames {frames: vec![ + "โ–นโ–นโ–นโ–นโ–น", + "โ–ธโ–นโ–นโ–นโ–น", + "โ–นโ–ธโ–นโ–นโ–น", + "โ–นโ–นโ–ธโ–นโ–น", + "โ–นโ–นโ–นโ–ธโ–น", + "โ–นโ–นโ–นโ–นโ–ธ" + ], interval: 120}, + Spinners::BouncingBar => SpinnerFrames {frames: vec![ + "[ ]", + "[= ]", + "[== ]", + "[=== ]", + "[ ===]", + "[ ==]", + "[ =]", + "[ ]", + "[ =]", + "[ ==]", + "[ ===]", + "[====]", + "[=== ]", + "[== ]", + "[= ]" + ], interval: 80}, + Spinners::BouncingBall => SpinnerFrames {frames: vec![ + "( โ— )", + "( โ— )", + "( โ— )", + "( โ— )", + "( โ—)", + "( โ— )", + "( โ— )", + "( โ— )", + "( โ— )", + "(โ— )" + ], interval: 80}, + Spinners::Smiley => SpinnerFrames {frames: vec![ + "๐Ÿ˜„ ", + "๐Ÿ˜ " + ], interval: 200}, + Spinners::Monkey => SpinnerFrames {frames: vec![ + "๐Ÿ™ˆ ", + "๐Ÿ™ˆ ", + "๐Ÿ™‰ ", + "๐Ÿ™Š " + ], interval: 300}, + Spinners::Hearts => SpinnerFrames {frames: vec![ + "๐Ÿ’› ", + "๐Ÿ’™ ", + "๐Ÿ’œ ", + "๐Ÿ’š ", + "โค๏ธ " + ], interval: 100}, + Spinners::Clock => SpinnerFrames {frames: vec![ + "๐Ÿ•› ", + "๐Ÿ• ", + "๐Ÿ•‘ ", + "๐Ÿ•’ ", + "๐Ÿ•“ ", + "๐Ÿ•” ", + "๐Ÿ•• ", + "๐Ÿ•– ", + "๐Ÿ•— ", + "๐Ÿ•˜ ", + "๐Ÿ•™ ", + "๐Ÿ•š " + ], interval: 100}, + Spinners::Earth => SpinnerFrames {frames: vec![ + "๐ŸŒ ", + "๐ŸŒŽ ", + "๐ŸŒ " + ], interval: 180}, + Spinners::Material => SpinnerFrames {frames: vec![ + "โ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–", + "โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–", + "โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–", + "โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–", + "โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–", + "โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–", + "โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–", + "โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–", + "โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–", + "โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆ", + "โ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆ", + "โ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆ", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆ", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆ", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆ", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆ", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–", + "โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–โ–", + "โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–", + "โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–โ–", + "โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–โ–", + "โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–", + "โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–", + "โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆโ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–ˆ", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–", + "โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–โ–" + ], interval: 17}, + Spinners::Moon => SpinnerFrames {frames: vec![ + "๐ŸŒ‘ ", + "๐ŸŒ’ ", + "๐ŸŒ“ ", + "๐ŸŒ” ", + "๐ŸŒ• ", + "๐ŸŒ– ", + "๐ŸŒ— ", + "๐ŸŒ˜ " + ], interval: 80}, + Spinners::Runner => SpinnerFrames {frames: vec![ + "๐Ÿšถ ", + "๐Ÿƒ " + ], interval: 140}, + Spinners::Pong => SpinnerFrames {frames: vec![ + "โ–โ ‚ โ–Œ", + "โ–โ ˆ โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ   โ–Œ", + "โ– โก€ โ–Œ", + "โ– โ   โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ ˆ โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ   โ–Œ", + "โ– โก€ โ–Œ", + "โ– โ   โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ ˆ โ–Œ", + "โ– โ ‚โ–Œ", + "โ– โ  โ–Œ", + "โ– โก€โ–Œ", + "โ– โ   โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ ˆ โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ   โ–Œ", + "โ– โก€ โ–Œ", + "โ– โ   โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ ˆ โ–Œ", + "โ– โ ‚ โ–Œ", + "โ– โ   โ–Œ", + "โ– โก€ โ–Œ", + "โ–โ   โ–Œ" + ], interval: 80}, + Spinners::Shark => SpinnerFrames {frames: vec![ + "โ–|\\____________โ–Œ", + "โ–_|\\___________โ–Œ", + "โ–__|\\__________โ–Œ", + "โ–___|\\_________โ–Œ", + "โ–____|\\________โ–Œ", + "โ–_____|\\_______โ–Œ", + "โ–______|\\______โ–Œ", + "โ–_______|\\_____โ–Œ", + "โ–________|\\____โ–Œ", + "โ–_________|\\___โ–Œ", + "โ–__________|\\__โ–Œ", + "โ–___________|\\_โ–Œ", + "โ–____________|\\โ–Œ", + "โ–____________/|โ–Œ", + "โ–___________/|_โ–Œ", + "โ–__________/|__โ–Œ", + "โ–_________/|___โ–Œ", + "โ–________/|____โ–Œ", + "โ–_______/|_____โ–Œ", + "โ–______/|______โ–Œ", + "โ–_____/|_______โ–Œ", + "โ–____/|________โ–Œ", + "โ–___/|_________โ–Œ", + "โ–__/|__________โ–Œ", + "โ–_/|___________โ–Œ", + "โ–/|____________โ–Œ" + ], interval: 120}, + Spinners::Dqpb => SpinnerFrames {frames: vec![ + "d", + "q", + "p", + "b" + ], interval: 100}, + Spinners::Weather => SpinnerFrames {frames: vec![ + "โ˜€๏ธ ", + "โ˜€๏ธ ", + "โ˜€๏ธ ", + "๐ŸŒค ", + "โ›…๏ธ ", + "๐ŸŒฅ ", + "โ˜๏ธ ", + "๐ŸŒง ", + "๐ŸŒจ ", + "๐ŸŒง ", + "๐ŸŒจ ", + "๐ŸŒง ", + "๐ŸŒจ ", + "โ›ˆ ", + "๐ŸŒจ ", + "๐ŸŒง ", + "๐ŸŒจ ", + "โ˜๏ธ ", + "๐ŸŒฅ ", + "โ›…๏ธ ", + "๐ŸŒค ", + "โ˜€๏ธ ", + "โ˜€๏ธ " + ], interval: 100}, + Spinners::Christmas => SpinnerFrames {frames: vec![ + "๐ŸŒฒ", + "๐ŸŽ„" + ], interval: 400}, + Spinners::Grenade => SpinnerFrames {frames: vec![ + "ุŒ ", + "โ€ฒ ", + " ยด ", + " โ€พ ", + " โธŒ", + " โธŠ", + " |", + " โŽ", + " โ•", + " เทด ", + " โ“", + " ", + " ", + " " + ], interval: 80}, + Spinners::Point => SpinnerFrames {frames: vec![ + "โˆ™โˆ™โˆ™", + "โ—โˆ™โˆ™", + "โˆ™โ—โˆ™", + "โˆ™โˆ™โ—", + "โˆ™โˆ™โˆ™" + ], interval: 125}, + Spinners::Layer => SpinnerFrames {frames: vec![ + "-", + "=", + "โ‰ก" + ], interval: 150}, + Spinners::BetaWave => SpinnerFrames {frames: vec![ + "ฯฮฒฮฒฮฒฮฒฮฒฮฒ", + "ฮฒฯฮฒฮฒฮฒฮฒฮฒ", + "ฮฒฮฒฯฮฒฮฒฮฒฮฒ", + "ฮฒฮฒฮฒฯฮฒฮฒฮฒ", + "ฮฒฮฒฮฒฮฒฯฮฒฮฒ", + "ฮฒฮฒฮฒฮฒฮฒฯฮฒ", + "ฮฒฮฒฮฒฮฒฮฒฮฒฯ" + ], interval: 80}, + Spinners::FingerDance => SpinnerFrames {frames: vec![ + "๐Ÿค˜ ", + "๐ŸคŸ ", + "๐Ÿ–– ", + "โœ‹ ", + "๐Ÿคš ", + "๐Ÿ‘† " + ], interval: 160}, + Spinners::FistBump => SpinnerFrames {frames: vec![ + "๐Ÿคœใ€€ใ€€ใ€€ใ€€๐Ÿค› ", + "๐Ÿคœใ€€ใ€€ใ€€ใ€€๐Ÿค› ", + "๐Ÿคœใ€€ใ€€ใ€€ใ€€๐Ÿค› ", + "ใ€€๐Ÿคœใ€€ใ€€๐Ÿค›ใ€€ ", + "ใ€€ใ€€๐Ÿคœ๐Ÿค›ใ€€ใ€€ ", + "ใ€€๐Ÿคœโœจ๐Ÿค›ใ€€ใ€€ ", + "๐Ÿคœใ€€โœจใ€€๐Ÿค›ใ€€ " + ], interval: 80}, + Spinners::SoccerHeader => SpinnerFrames {frames: vec![ + " ๐Ÿง‘โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ ", + "๐Ÿง‘ โšฝ๏ธ ๐Ÿง‘ " + ], interval: 80}, + Spinners::Mindblown => SpinnerFrames {frames: vec![ + "๐Ÿ˜ ", + "๐Ÿ˜ ", + "๐Ÿ˜ฎ ", + "๐Ÿ˜ฎ ", + "๐Ÿ˜ฆ ", + "๐Ÿ˜ฆ ", + "๐Ÿ˜ง ", + "๐Ÿ˜ง ", + "๐Ÿคฏ ", + "๐Ÿ’ฅ ", + "โœจ ", + "ใ€€ ", + "ใ€€ ", + "ใ€€ " + ], interval: 160}, + Spinners::Speaker => SpinnerFrames {frames: vec![ + "๐Ÿ”ˆ ", + "๐Ÿ”‰ ", + "๐Ÿ”Š ", + "๐Ÿ”‰ " + ], interval: 160}, + Spinners::OrangePulse => SpinnerFrames {frames: vec![ + "๐Ÿ”ธ ", + "๐Ÿ”ถ ", + "๐ŸŸ  ", + "๐ŸŸ  ", + "๐Ÿ”ถ " + ], interval: 100}, + Spinners::BluePulse => SpinnerFrames {frames: vec![ + "๐Ÿ”น ", + "๐Ÿ”ท ", + "๐Ÿ”ต ", + "๐Ÿ”ต ", + "๐Ÿ”ท " + ], interval: 100}, + Spinners::OrangeBluePulse => SpinnerFrames {frames: vec![ + "๐Ÿ”ธ ", + "๐Ÿ”ถ ", + "๐ŸŸ  ", + "๐ŸŸ  ", + "๐Ÿ”ถ ", + "๐Ÿ”น ", + "๐Ÿ”ท ", + "๐Ÿ”ต ", + "๐Ÿ”ต ", + "๐Ÿ”ท " + ], interval: 100}, + Spinners::TimeTravel => SpinnerFrames {frames: vec![ + "๐Ÿ•› ", + "๐Ÿ•š ", + "๐Ÿ•™ ", + "๐Ÿ•˜ ", + "๐Ÿ•— ", + "๐Ÿ•– ", + "๐Ÿ•• ", + "๐Ÿ•” ", + "๐Ÿ•“ ", + "๐Ÿ•’ ", + "๐Ÿ•‘ ", + "๐Ÿ• " + ], interval: 100}, + Spinners::Aesthetic => SpinnerFrames {frames: vec![ + "โ–ฐโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑ", + "โ–ฐโ–ฐโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑ", + "โ–ฐโ–ฐโ–ฐโ–ฑโ–ฑโ–ฑโ–ฑ", + "โ–ฐโ–ฐโ–ฐโ–ฐโ–ฑโ–ฑโ–ฑ", + "โ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฑโ–ฑ", + "โ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฑ", + "โ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐ", + "โ–ฐโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑ" + ], interval: 80} + } + }; +} diff --git a/src/utils/spinner_enum.rs b/src/utils/spinner_enum.rs new file mode 100644 index 0000000..53a44e5 --- /dev/null +++ b/src/utils/spinner_enum.rs @@ -0,0 +1,89 @@ +use strum::EnumIter; +/// An enum of all the available spinners. +/// Contains around 80+ spinners. +/// Each variant in this enum is assigned to a HashMap holding it's frames and interval count. +#[derive(Debug, Eq, Hash, PartialEq, EnumIter)] +pub enum Spinners { + Dots, + Dots2, + Dots3, + Dots4, + Dots5, + Dots6, + Dots7, + Dots8, + Dots9, + Dots10, + Dots11, + Dots12, + Dots8Bit, + Line, + Line2, + Pipe, + SimpleDots, + SimpleDotsScrolling, + Star, + Star2, + Flip, + Hamburger, + GrowVertical, + GrowHorizontal, + Balloon, + Balloon2, + Noise, + Bounce, + BoxBounce, + BoxBounce2, + Triangle, + Arc, + Circle, + SquareCorners, + CircleQuarters, + CircleHalves, + Squish, + Toggle, + Toggle2, + Toggle3, + Toggle4, + Toggle5, + Toggle6, + Toggle7, + Toggle8, + Toggle9, + Toggle10, + Toggle11, + Toggle12, + Toggle13, + Arrow, + Arrow2, + Arrow3, + BouncingBar, + BouncingBall, + Smiley, + Monkey, + Hearts, + Clock, + Earth, + Material, + Moon, + Runner, + Pong, + Shark, + Dqpb, + Weather, + Christmas, + Grenade, + Point, + Layer, + BetaWave, + FingerDance, + FistBump, + SoccerHeader, + Mindblown, + Speaker, + OrangePulse, + BluePulse, + OrangeBluePulse, + TimeTravel, + Aesthetic, +}