cliclack 0.5.4

Beautiful, minimal, opinionated CLI prompts inspired by the Clack NPM package.
Documentation
use std::{sync::mpsc::channel, time::Duration};

use cliclack::{clear_screen, intro, log::remark, multi_progress, outro, progress_bar, spinner};
use console::{style, Term};
use rand::random_range;

enum Message {
    Interrupt,
}

fn main() -> std::io::Result<()> {
    let (tx, rx) = channel();

    // Set a no-op Ctrl-C handler which allows to catch
    // `ErrorKind::Interrupted` error on `term.read_key()`.
    ctrlc::set_handler(move || {
        tx.send(Message::Interrupt).ok();
    })
    .expect("setting Ctrl-C handler");

    // Clear the screen and print the header.
    clear_screen()?;
    intro(style(" progress bar ").on_cyan().black())?;
    remark("Press Ctrl-C")?;

    // Create a new progress bar and set the text to "Installation".
    let multi = multi_progress("Doing stuff...");

    let spinner = multi.add(spinner());
    spinner.start("Establish connection...");
    std::thread::sleep(Duration::from_secs(1));
    spinner.clear();

    let pb1 = multi.add(progress_bar(500));
    let pb2 = multi.add(progress_bar(500));
    let pb3 = multi.insert(multi.length() - 1, progress_bar(500));

    pb1.start("Downloading files...");
    pb2.start("Copying files...");
    pb3.start("Verifying download...");

    // Simulate doing some stuff....
    while !pb1.is_finished() || !pb2.is_finished() || !pb3.is_finished() {
        // Use a random timeout to simulate some work.
        let timeout = Duration::from_millis(random_range(10..75));

        // Check if we received a message from the channel.
        if let Ok(Message::Interrupt) = rx.recv_timeout(timeout) {
            // Clear the garbage appearing because of Ctrl-C.
            let term = Term::stderr();
            term.clear_line()?;
            term.move_cursor_up(1)?;

            pb1.cancel(format!("{} Downloading files", style("").red()));
            pb2.cancel(format!("{} Copying files", style("").red()));
            pb3.cancel(format!("{} Verifying download", style("").red()));
            multi.cancel();
            return Ok(());
        }

        if pb1.position() < pb1.length().unwrap() {
            pb1.inc(random_range(1..20));
        } else if !pb1.is_finished() {
            pb1.stop(format!("{} Downloading files", style("").green()));
        }

        if pb3.position() < pb2.length().unwrap() {
            pb2.inc(random_range(1..13));
        } else if !pb2.is_finished() {
            pb2.stop(format!("{} Copying files", style("").green()));
        }

        if pb3.position() < pb3.length().unwrap() {
            pb3.set_position(pb3.position() + random_range(1..16));
        } else if !pb3.is_finished() {
            pb3.stop(format!("{} Verifying download", style("").green()));
        }
    }

    multi.stop();
    outro("Done!")?;

    Ok(())
}