elio 1.6.0

Snappy, batteries-included terminal file manager with rich previews, inline images, bulk actions, and trash support.
Documentation
use super::super::rules::rgb;
use super::*;

const ALTERNATE_EXAMPLE_THEME_NAMES: &[&str] = &[
    "default-light",
    "blush-light",
    "amber-dusk",
    "catppuccin-mocha",
    "tokyo-night",
    "navi",
    "neon-cherry",
    "transparent",
];

const GENERIC_DEV_DIRECTORIES: &[&str] = &[
    "node_modules",
    "tests",
    "test",
    "__tests__",
    "scripts",
    "build",
    "dist",
    ".next",
    ".nuxt",
    ".svelte-kit",
    ".astro",
    "assets",
    "coverage",
    "tmp",
    "temp",
    "out",
    "target",
    "bin",
    "lib",
    "vendor",
    "src",
    "config",
    "docs",
];

fn alternate_example_theme_config(name: &str) -> &'static str {
    match name {
        "default-light" => include_str!("../../../../../examples/themes/default-light/theme.toml"),
        "blush-light" => include_str!("../../../../../examples/themes/blush-light/theme.toml"),
        "amber-dusk" => include_str!("../../../../../examples/themes/amber-dusk/theme.toml"),
        "catppuccin-mocha" => {
            include_str!("../../../../../examples/themes/catppuccin-mocha/theme.toml")
        }
        "tokyo-night" => include_str!("../../../../../examples/themes/tokyo-night/theme.toml"),
        "navi" => include_str!("../../../../../examples/themes/navi/theme.toml"),
        "neon-cherry" => include_str!("../../../../../examples/themes/neon-cherry/theme.toml"),
        "transparent" => include_str!("../../../../../examples/themes/transparent/theme.toml"),
        _ => panic!("unknown alternate example theme fixture: {name}"),
    }
}

fn load_alternate_example_theme(name: &str) -> Theme {
    Theme::from_config_str(alternate_example_theme_config(name)).unwrap_or_else(|error| {
        panic!("{name} example theme should parse as a user theme: {error}")
    })
}

fn assert_symlink_directory_matches_folder_color(theme: &Theme, label: &str) {
    let directory = theme.classes.get(&FileClass::Directory).unwrap();
    let symlink_directory = theme.classes.get(&FileClass::SymlinkDirectory).unwrap();

    assert_eq!(
        symlink_directory.icon, "",
        "{label}: symlinked directories should use the linked-folder icon",
    );
    assert_eq!(
        symlink_directory.color, directory.color,
        "{label}: symlinked directories should use the normal folder color",
    );
}

fn assert_uses_normal_folder_color_for_generic_dev_directories(theme: &Theme, label: &str) {
    let normal_folder_color = theme
        .resolve(Path::new("projects"), EntryKind::Directory)
        .color;

    for directory in GENERIC_DEV_DIRECTORIES {
        let resolved = theme.resolve(Path::new(directory), EntryKind::Directory);
        assert_eq!(
            resolved.class,
            FileClass::Directory,
            "{label}: {directory} should resolve as a directory",
        );
        assert_eq!(
            resolved.color, normal_folder_color,
            "{label}: {directory} should use the normal folder color",
        );
    }
}

#[test]
fn alternate_example_themes_style_symlinks_like_their_base_kinds() {
    for name in ALTERNATE_EXAMPLE_THEME_NAMES {
        let theme = load_alternate_example_theme(name);
        assert_symlink_directory_matches_folder_color(&theme, name);
    }
}

#[test]
fn blush_light_example_theme_parses_as_user_theme_and_applies_custom_icon_and_code_colors() {
    let theme = load_alternate_example_theme("blush-light");

    assert_eq!(theme.preview.code.keyword, rgb(0xd8, 0x63, 0x92));
    assert_eq!(theme.preview.code.function, rgb(0x8f, 0x71, 0xbf));

    let directory = theme.resolve(Path::new("projects"), EntryKind::Directory);
    assert_eq!(directory.class, FileClass::Directory);
    assert_eq!(directory.icon, "󰉋");
    assert_eq!(directory.color, rgb(0xd4, 0x6b, 0x93));

    let rust = theme.resolve(Path::new("main.rs"), EntryKind::File);
    assert_eq!(rust.class, FileClass::Code);
    assert_eq!(rust.icon, "");
    assert_eq!(rust.color, rgb(0xca, 0x81, 0x68));

    let readme = theme.resolve(Path::new("README.md"), EntryKind::File);
    assert_eq!(readme.class, FileClass::Document);
    assert_eq!(readme.icon, "");
    assert_eq!(readme.color, rgb(0xbb, 0x90, 0x7b));
}

#[test]
fn default_light_example_theme_parses_as_user_theme_and_preserves_default_icon_mappings() {
    let theme = load_alternate_example_theme("default-light");

    assert_eq!(theme.palette.bg, rgb(0xef, 0xf2, 0xf5));
    assert_eq!(theme.preview.code.keyword, rgb(0x7a, 0xae, 0xff));
    assert_eq!(theme.preview.code.function, rgb(0x46, 0x9f, 0xc3));
    assert_eq!(theme.preview.code.string, rgb(0x4d, 0x92, 0x79));
    assert_eq!(theme.preview.code.r#type, rgb(0x8a, 0x74, 0xc8));

    let directory = theme.resolve(Path::new("projects"), EntryKind::Directory);
    assert_eq!(directory.class, FileClass::Directory);
    assert_eq!(directory.icon, "󰉋");
    assert_eq!(directory.color, rgb(0x5b, 0xa8, 0xff));

    let downloads = theme.resolve(Path::new("Downloads"), EntryKind::Directory);
    assert_eq!(downloads.class, FileClass::Directory);
    assert_eq!(downloads.icon, "󰉍");
    assert_eq!(downloads.color, rgb(0xb9, 0x97, 0x3e));

    let pictures = theme.resolve(Path::new("Pictures"), EntryKind::Directory);
    assert_eq!(pictures.class, FileClass::Directory);
    assert_eq!(pictures.icon, "󰉏");
    assert_eq!(pictures.color, rgb(0x55, 0xa7, 0x9e));

    let music = theme.resolve(Path::new("Music"), EntryKind::Directory);
    assert_eq!(music.class, FileClass::Directory);
    assert_eq!(music.icon, "󱍙");
    assert_eq!(music.color, rgb(0x9a, 0x81, 0xcf));

    let src = theme.resolve(Path::new("src"), EntryKind::Directory);
    assert_eq!(src.class, FileClass::Directory);
    assert_eq!(src.icon, "󰉋");
    assert_eq!(src.color, rgb(0x5b, 0xa8, 0xff));

    let shell = theme.resolve(Path::new("deploy.sh"), EntryKind::File);
    assert_eq!(shell.class, FileClass::Code);
    assert_eq!(shell.icon, "");
    assert_eq!(shell.color, rgb(0x69, 0x78, 0x8b));

    let rust = theme.resolve(Path::new("main.rs"), EntryKind::File);
    assert_eq!(rust.class, FileClass::Code);
    assert_eq!(rust.icon, "");
    assert_eq!(rust.color, rgb(0xb8, 0x74, 0x45));

    let package = theme.resolve(Path::new("package.json"), EntryKind::File);
    assert_eq!(package.class, FileClass::Config);
    assert_eq!(package.icon, "󰏗");
    assert_eq!(package.color, rgb(0x7d, 0xb0, 0xff));

    let readme = theme.resolve(Path::new("README.md"), EntryKind::File);
    assert_eq!(readme.class, FileClass::Document);
    assert_eq!(readme.icon, "");
    assert_eq!(readme.color, rgb(0xab, 0x97, 0x7a));

    let turbo = theme.resolve(Path::new("turbo.json"), EntryKind::File);
    assert_eq!(turbo.class, FileClass::Config);
    assert_eq!(turbo.icon, "󰐷");
    assert_eq!(turbo.color, rgb(0x72, 0x81, 0x95));
}

#[test]
fn amber_dusk_example_theme_parses_as_user_theme_and_applies_warm_dark_palette() {
    let theme = load_alternate_example_theme("amber-dusk");

    assert_eq!(theme.palette.bg, rgb(0x12, 0x0f, 0x0d));
    assert_eq!(theme.preview.code.keyword, rgb(0xcf, 0x98, 0x51));
    assert_eq!(theme.preview.code.function, rgb(0x7f, 0xa7, 0xa5));

    let directory = theme.resolve(Path::new("projects"), EntryKind::Directory);
    assert_eq!(directory.class, FileClass::Directory);
    assert_eq!(directory.icon, "󰉋");
    assert_eq!(directory.color, rgb(0xcf, 0x9c, 0x67));

    let downloads = theme.resolve(Path::new("Downloads"), EntryKind::Directory);
    assert_eq!(downloads.class, FileClass::Directory);
    assert_eq!(downloads.icon, "󰉍");
    assert_eq!(downloads.color, rgb(0xd4, 0xa4, 0x66));

    let src = theme.resolve(Path::new("src"), EntryKind::Directory);
    assert_eq!(src.class, FileClass::Directory);
    assert_eq!(src.icon, "󰉋");
    assert_eq!(src.color, rgb(0xcf, 0x9c, 0x67));

    let vendor = theme.resolve(Path::new("vendor"), EntryKind::Directory);
    assert_eq!(vendor.class, FileClass::Directory);
    assert_eq!(vendor.icon, "󰉋");
    assert_eq!(vendor.color, rgb(0xcf, 0x9c, 0x67));

    let rust = theme.resolve(Path::new("main.rs"), EntryKind::File);
    assert_eq!(rust.class, FileClass::Code);
    assert_eq!(rust.icon, "");
    assert_eq!(rust.color, rgb(0xc5, 0x8a, 0x5e));
}

#[test]
fn catppuccin_mocha_example_theme_parses_as_user_theme_and_applies_palette_consistently() {
    let theme = load_alternate_example_theme("catppuccin-mocha");

    assert_eq!(theme.palette.bg, rgb(0x1e, 0x1e, 0x2e));
    assert_eq!(theme.palette.selected_bg, rgb(0x45, 0x47, 0x5a));
    assert_ne!(theme.palette.selected_bg, theme.palette.surface);
    assert_eq!(theme.preview.code.keyword, rgb(0xcb, 0xa6, 0xf7));
    assert_eq!(theme.preview.code.function, rgb(0x89, 0xb4, 0xfa));
    assert_eq!(theme.preview.code.string, rgb(0xa6, 0xe3, 0xa1));
    assert_eq!(theme.preview.code.r#type, rgb(0xf9, 0xe2, 0xaf));

    let directory = theme.resolve(Path::new("projects"), EntryKind::Directory);
    assert_eq!(directory.class, FileClass::Directory);
    assert_eq!(directory.icon, "󰉋");
    assert_eq!(directory.color, rgb(0x89, 0xb4, 0xfa));

    let downloads = theme.resolve(Path::new("Downloads"), EntryKind::Directory);
    assert_eq!(downloads.class, FileClass::Directory);
    assert_eq!(downloads.icon, "󰉍");
    assert_eq!(downloads.color, rgb(0xf9, 0xe2, 0xaf));

    let pictures = theme.resolve(Path::new("Pictures"), EntryKind::Directory);
    assert_eq!(pictures.class, FileClass::Directory);
    assert_eq!(pictures.icon, "󰉏");
    assert_eq!(pictures.color, rgb(0x94, 0xe2, 0xd5));

    let music = theme.resolve(Path::new("Music"), EntryKind::Directory);
    assert_eq!(music.class, FileClass::Directory);
    assert_eq!(music.icon, "󱍙");
    assert_eq!(music.color, rgb(0xcb, 0xa6, 0xf7));

    let src = theme.resolve(Path::new("src"), EntryKind::Directory);
    assert_eq!(src.class, FileClass::Directory);
    assert_eq!(src.icon, "󰉋");
    assert_eq!(src.color, rgb(0x89, 0xb4, 0xfa));

    let rust = theme.resolve(Path::new("main.rs"), EntryKind::File);
    assert_eq!(rust.class, FileClass::Code);
    assert_eq!(rust.icon, "");
    assert_eq!(rust.color, rgb(0xfa, 0xb3, 0x87));

    let package = theme.resolve(Path::new("package.json"), EntryKind::File);
    assert_eq!(package.class, FileClass::Config);
    assert_eq!(package.icon, "󰏗");
    assert_eq!(package.color, rgb(0x89, 0xb4, 0xfa));

    let readme = theme.resolve(Path::new("README.md"), EntryKind::File);
    assert_eq!(readme.class, FileClass::Document);
    assert_eq!(readme.icon, "");
    assert_eq!(readme.color, rgb(0xf9, 0xe2, 0xaf));
}

#[test]
fn tokyo_night_example_theme_parses_as_user_theme_and_applies_palette_consistently() {
    let theme = load_alternate_example_theme("tokyo-night");

    assert_eq!(theme.palette.bg, rgb(0x1a, 0x1b, 0x26));
    assert_eq!(theme.preview.code.keyword, rgb(0xbb, 0x9a, 0xf7));
    assert_eq!(theme.preview.code.function, rgb(0x7d, 0xcf, 0xff));
    assert_eq!(theme.preview.code.string, rgb(0x9e, 0xce, 0x6a));
    assert_eq!(theme.preview.code.r#type, rgb(0xe0, 0xaf, 0x68));

    let directory = theme.resolve(Path::new("projects"), EntryKind::Directory);
    assert_eq!(directory.class, FileClass::Directory);
    assert_eq!(directory.icon, "󰉋");
    assert_eq!(directory.color, rgb(0x7a, 0xa2, 0xf7));

    let downloads = theme.resolve(Path::new("Downloads"), EntryKind::Directory);
    assert_eq!(downloads.class, FileClass::Directory);
    assert_eq!(downloads.icon, "󰉍");
    assert_eq!(downloads.color, rgb(0xe0, 0xaf, 0x68));

    let pictures = theme.resolve(Path::new("Pictures"), EntryKind::Directory);
    assert_eq!(pictures.class, FileClass::Directory);
    assert_eq!(pictures.icon, "󰉏");
    assert_eq!(pictures.color, rgb(0x73, 0xda, 0xca));

    let music = theme.resolve(Path::new("Music"), EntryKind::Directory);
    assert_eq!(music.class, FileClass::Directory);
    assert_eq!(music.icon, "󱍙");
    assert_eq!(music.color, rgb(0xbb, 0x9a, 0xf7));

    let src = theme.resolve(Path::new("src"), EntryKind::Directory);
    assert_eq!(src.class, FileClass::Directory);
    assert_eq!(src.icon, "󰉋");
    assert_eq!(src.color, rgb(0x7a, 0xa2, 0xf7));

    let rust = theme.resolve(Path::new("main.rs"), EntryKind::File);
    assert_eq!(rust.class, FileClass::Code);
    assert_eq!(rust.icon, "");
    assert_eq!(rust.color, rgb(0xff, 0x9e, 0x64));

    let package = theme.resolve(Path::new("package.json"), EntryKind::File);
    assert_eq!(package.class, FileClass::Config);
    assert_eq!(package.icon, "󰏗");
    assert_eq!(package.color, rgb(0x7a, 0xa2, 0xf7));

    let readme = theme.resolve(Path::new("README.md"), EntryKind::File);
    assert_eq!(readme.class, FileClass::Document);
    assert_eq!(readme.icon, "");
    assert_eq!(readme.color, rgb(0xe0, 0xaf, 0x68));
}

#[test]
fn alternate_example_themes_use_normal_folder_color_for_generic_dev_directories() {
    for label in ALTERNATE_EXAMPLE_THEME_NAMES {
        let theme = load_alternate_example_theme(label);
        assert_uses_normal_folder_color_for_generic_dev_directories(&theme, label);
    }
}