ggsci 0.6.2

Scientific and sci-fi color palettes from ggsci for Rust
Documentation
use std::{collections::HashSet, str::FromStr};

use ggsci::{
    iterm_palette, iterm_palette_count, iterm_palette_names, iterm_palettes,
    iterm_total_color_count, palettes, palettes_by_kind, total_color_count, Error, ItermChannel,
    ItermVariant, PaletteKind, ITERM_CHANNELS,
};

const ROSE_PINE: [&str; 6] = [
    "#9CCFD8", "#F6C177", "#EB6F92", "#EBBCBA", "#31748F", "#C4A7E7",
];

const DRACULA_NORMAL: [&str; 6] = [
    "#BD93F9", "#F1FA8C", "#FF5555", "#8BE9FD", "#50FA7B", "#FF79C6",
];

const DRACULA_BRIGHT: [&str; 6] = [
    "#D6ACFF", "#FFFFA5", "#FF6E6E", "#A4FFFF", "#69FF94", "#FF92DF",
];

#[test]
fn generated_iterm_registry_has_expected_counts_and_shape() {
    assert_eq!(iterm_palette_count(), 551);
    assert_eq!(iterm_palettes().len(), 551);
    assert_eq!(iterm_total_color_count(), 6_612);

    for palette in iterm_palettes() {
        assert_eq!(palette.kind(), PaletteKind::Discrete);
        assert_eq!(palette.colors(ItermVariant::Normal).len(), 6);
        assert_eq!(palette.colors(ItermVariant::Bright).len(), 6);
    }
}

#[test]
fn channels_have_canonical_order_names_and_indices() {
    assert_eq!(
        ITERM_CHANNELS,
        [
            ItermChannel::Blue,
            ItermChannel::Yellow,
            ItermChannel::Red,
            ItermChannel::Cyan,
            ItermChannel::Green,
            ItermChannel::Magenta,
        ]
    );

    let names = ITERM_CHANNELS.map(ItermChannel::as_str);
    assert_eq!(names, ["Blue", "Yellow", "Red", "Cyan", "Green", "Magenta"]);
    for (index, channel) in ITERM_CHANNELS.into_iter().enumerate() {
        assert_eq!(channel.index(), index);
    }
}

#[test]
fn canonical_names_are_unique_and_keep_upstream_order() {
    let names = iterm_palette_names().collect::<Vec<_>>();
    let unique = names.iter().copied().collect::<HashSet<_>>();
    assert_eq!(unique.len(), names.len());
    assert_eq!(names.first(), Some(&"0x96f"));
    assert_eq!(names.last(), Some(&"Zenwritten Light"));
}

#[test]
fn lookup_normalizes_case_and_separators_but_preserves_punctuation() {
    for name in ["Rose Pine", "rose-pine", "ROSE_PINE", "rose\tpine"] {
        assert_eq!(iterm_palette(name).unwrap().name(), "Rose Pine");
    }
    assert_eq!(iterm_palette("0x96f").unwrap().name(), "0x96f");

    let dracula = iterm_palette("Dracula").unwrap();
    let dracula_plus = iterm_palette("Dracula+").unwrap();
    assert_eq!(dracula.name(), "Dracula");
    assert_eq!(dracula_plus.name(), "Dracula+");
    assert_ne!(dracula, dracula_plus);
}

#[test]
fn rose_pine_matches_r_for_both_variants() {
    let rose_pine = iterm_palette("Rose Pine").unwrap();
    assert_eq!(
        rose_pine.take_hex(ItermVariant::Normal, 6).unwrap(),
        ROSE_PINE
    );
    assert_eq!(
        rose_pine.take_hex(ItermVariant::Bright, 6).unwrap(),
        ROSE_PINE
    );

    for channel in ITERM_CHANNELS {
        assert_eq!(
            rose_pine.color(ItermVariant::Normal, channel),
            rose_pine.colors(ItermVariant::Normal)[channel.index()]
        );
    }
}

#[test]
fn dracula_matches_r_and_variants_differ() {
    let dracula = iterm_palette("Dracula").unwrap();
    assert_eq!(
        dracula.take_hex(ItermVariant::Normal, 6).unwrap(),
        DRACULA_NORMAL
    );
    assert_eq!(
        dracula.take_hex(ItermVariant::Bright, 6).unwrap(),
        DRACULA_BRIGHT
    );
    assert_ne!(
        dracula.colors(ItermVariant::Normal),
        dracula.colors(ItermVariant::Bright)
    );
}

#[test]
fn take_obeys_fixed_discrete_length_without_cycling() {
    let rose_pine = iterm_palette("Rose Pine").unwrap();
    assert!(rose_pine.take(ItermVariant::Normal, 0).unwrap().is_empty());
    assert_eq!(
        rose_pine.take(ItermVariant::Normal, 6).unwrap(),
        rose_pine.colors(ItermVariant::Normal)
    );
    assert_eq!(
        rose_pine.take(ItermVariant::Normal, 7),
        Err(Error::TooManyItermColorsRequested {
            palette: "Rose Pine",
            variant: "normal",
            requested: 7,
            available: 6,
        })
    );
}

#[test]
fn cycle_is_explicitly_infinite() {
    let palette = iterm_palette("0x96f").unwrap();
    let cycled = palette
        .cycle(ItermVariant::Bright)
        .take(8)
        .collect::<Vec<_>>();
    assert_eq!(cycled[..6], palette.colors(ItermVariant::Bright)[..]);
    assert_eq!(cycled[6], cycled[0]);
    assert_eq!(cycled[7], cycled[1]);
}

#[test]
fn variant_parsing_is_case_insensitive() {
    assert_eq!(ItermVariant::parse("normal").unwrap(), ItermVariant::Normal);
    assert_eq!(ItermVariant::parse("BRIGHT").unwrap(), ItermVariant::Bright);
    assert_eq!(
        ItermVariant::from_str(" Normal ").unwrap(),
        ItermVariant::Normal
    );
    assert_eq!(ItermVariant::Normal.as_str(), "normal");
    assert_eq!(ItermVariant::Bright.to_string(), "bright");
}

#[test]
fn lookup_and_variant_errors_are_specific() {
    assert_eq!(
        iterm_palette("missing"),
        Err(Error::UnknownItermPalette {
            palette: "missing".to_owned(),
        })
    );
    assert_eq!(
        ItermVariant::parse("dim"),
        Err(Error::UnknownItermVariant {
            variant: "dim".to_owned(),
        })
    );
}

#[test]
fn rgba_follows_r_alpha_semantics() {
    let palette = iterm_palette("Rose Pine").unwrap();
    let opaque = palette.take_rgba(ItermVariant::Normal, 6, 1.0).unwrap();
    assert!(opaque.iter().all(|color| color.a() == 255));

    let translucent = palette.take_rgba(ItermVariant::Bright, 6, 0.5).unwrap();
    assert!(translucent.iter().all(|color| color.a() == 127));

    for alpha in [0.0, -0.1, 1.1, f32::INFINITY, f32::NAN] {
        assert!(matches!(
            palette.take_rgba(ItermVariant::Normal, 1, alpha),
            Err(Error::InvalidAlpha { .. })
        ));
    }
}

#[test]
fn core_registry_counts_and_membership_remain_unchanged() {
    assert_eq!(palettes().len(), 86);
    assert_eq!(total_color_count(), 946);
    assert_eq!(palettes_by_kind(PaletteKind::Discrete).count(), 33);
    assert_eq!(palettes_by_kind(PaletteKind::Continuous).count(), 53);

    let iterm_names = iterm_palette_names().collect::<HashSet<_>>();
    assert!(palettes().iter().all(|palette| {
        palette.family() != "iterm"
            && !iterm_names.contains(palette.family())
            && !iterm_names.contains(palette.variant())
    }));
}