aisling 0.3.17

Embeddable terminal text effects and loaders for Rust TUI applications.
Documentation
use std::str::FromStr;

use aisling::{Loader, LoaderConfig, LoaderKind, LoaderProgress};

#[test]
fn exposes_loader_catalog() {
    assert_eq!(LoaderKind::all().len(), 69);
    for kind in LoaderKind::all() {
        assert_eq!(LoaderKind::from_str(kind.name()).unwrap(), *kind);
    }
    assert_eq!(
        LoaderKind::from_str("progress-bar").unwrap(),
        LoaderKind::Bar
    );
    assert_eq!(LoaderKind::from_str("spinner").unwrap(), LoaderKind::Line);
    assert_eq!(
        LoaderKind::from_str("decrypt-down").unwrap(),
        LoaderKind::Decrypt
    );
    assert_eq!(
        LoaderKind::from_str("laser").unwrap(),
        LoaderKind::LaserEtch
    );
    assert_eq!(
        LoaderKind::from_str("orbiting-volley").unwrap(),
        LoaderKind::OrbittingVolley
    );
    assert_eq!(
        LoaderKind::from_str("center-out").unwrap(),
        LoaderKind::MiddleOut
    );
}

#[test]
fn every_loader_renders_fixed_size_frames() {
    let config = LoaderConfig::default()
        .with_size(40, 4)
        .with_duration(6)
        .with_label("task");
    for kind in LoaderKind::all() {
        let loader = Loader::with_config(*kind, config.clone());
        let frame = loader.frame(3, 0.5);
        assert_eq!(frame.width(), 40, "{kind}");
        assert_eq!(frame.height(), 4, "{kind}");
    }
}

#[test]
fn tqdm_loader_formats_download_counts() {
    let loader = Loader::with_config(
        LoaderKind::Tqdm,
        LoaderConfig::default()
            .with_width(72)
            .with_label("download")
            .with_unit("B")
            .with_fraction(true),
    );
    let line = loader.line(2, LoaderProgress::from_counts(42, 100));
    assert!(line.contains("42%"));
    assert!(line.contains("42B/100B"));
    assert!(line.contains("download"));
}

#[test]
fn loader_iterator_advances_to_complete_progress() {
    let loader = Loader::with_config(
        LoaderKind::Bar,
        LoaderConfig::default().with_width(32).with_duration(4),
    );
    let frames = loader.frames();
    assert_eq!(frames.len(), 4);
    assert!(frames.last().unwrap().to_plain_string().contains("100%"));
}

#[test]
fn effect_loaders_emit_colorized_cells() {
    let config = LoaderConfig::default()
        .with_size(48, 5)
        .with_label("download");
    for kind in LoaderKind::all()
        .iter()
        .copied()
        .filter(|kind| kind.is_effect_style())
    {
        let frame = Loader::with_config(kind, config.clone()).frame(8, 0.45);
        assert!(
            frame.cells().iter().any(|cell| cell.colors.fg.is_some()),
            "{kind}"
        );
    }
}

#[test]
fn new_loader_variants_parse_aliases_and_render() {
    let cases = [
        ("fire-pulse", LoaderKind::Ember),
        ("cipher-spin", LoaderKind::Cipher),
        ("vhs-distort", LoaderKind::Glitch),
        ("swirl", LoaderKind::Vortex),
        ("storm-light", LoaderKind::StormFlicker),
        ("sweep-beam", LoaderKind::LaserSweep),
        ("cascading", LoaderKind::Cascade),
        ("pulse-grid", LoaderKind::GridPulse),
    ];
    let config = LoaderConfig::default()
        .with_size(48, 6)
        .with_duration(12)
        .with_label("new-loader");

    for (alias, kind) in cases {
        assert_eq!(LoaderKind::from_str(alias).unwrap(), kind);
        assert!(LoaderKind::all().contains(&kind));

        let frame = Loader::with_config(kind, config.clone()).frame(5, 0.5);
        assert_eq!(frame.width(), 48, "{kind}");
        assert_eq!(frame.height(), 6, "{kind}");
        assert!(frame.to_plain_string().contains("new-loader"), "{kind}");
        assert!(
            frame.cells().iter().any(|cell| cell.colors.fg.is_some()),
            "{kind}"
        );
    }
}