Bläddra i källkod

feat: Use perfect hash tables for file types and icons

Robert Minsk 2 år sedan
förälder
incheckning
c8c2b1c7d0
6 ändrade filer med 791 tillägg och 718 borttagningar
  1. 61 0
      Cargo.lock
  2. 8 0
      Cargo.toml
  3. 660 3
      build.rs
  4. 0 16
      src/fs/file.rs
  5. 49 285
      src/info/filetype.rs
  6. 13 414
      src/output/icons.rs

+ 61 - 0
Cargo.lock

@@ -93,6 +93,8 @@ dependencies = [
  "natord",
  "num_cpus",
  "number_prefix",
+ "phf",
+ "phf_codegen",
  "scoped_threadpool",
  "term_grid",
  "terminal_size",
@@ -293,12 +295,65 @@ version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
+[[package]]
+name = "phf"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
+dependencies = [
+ "siphasher",
+]
+
 [[package]]
 name = "pkg-config"
 version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
 
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
 [[package]]
 name = "redox_syscall"
 version = "0.1.57"
@@ -325,6 +380,12 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
 
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
 [[package]]
 name = "term_grid"
 version = "0.1.7"

+ 8 - 0
Cargo.toml

@@ -62,6 +62,10 @@ version = "0.18"
 optional = true
 default-features = false
 
+[dependencies.phf]
+version = "0.11.2"
+default-features = false
+
 [target.'cfg(unix)'.dependencies]
 uzers = "0.11.2"
 
@@ -69,6 +73,10 @@ uzers = "0.11.2"
 version = "0.5.2"
 default-features = false
 
+[build-dependencies.phf_codegen]
+version = "0.11.2"
+default-features = false
+
 [features]
 default = [ "git" ]
 git = [ "git2" ]

+ 660 - 3
build.rs

@@ -20,6 +20,13 @@ use datetime::{LocalDateTime, ISO};
 
 /// The build script entry point.
 fn main() -> io::Result<()> {
+    create_version_string_file()?;
+    create_file_typing_hash_file()?;
+    create_file_icon_hash_file()
+}
+
+/// Create the version_string.txt file
+fn create_version_string_file() -> io::Result<()> {
     #![allow(clippy::write_with_newline)]
 
     let tagline = "eza - A modern, maintained replacement for ls";
@@ -42,9 +49,7 @@ fn main() -> io::Result<()> {
 
     // Bland version text
     let mut f = File::create(path).unwrap_or_else(|_| { panic!("{}", path.to_string_lossy().to_string()) });
-    writeln!(f, "{}", strip_codes(&ver))?;
-
-    Ok(())
+    writeln!(f, "{}", strip_codes(&ver))
 }
 
 /// Removes escape codes from a string.
@@ -121,3 +126,655 @@ fn build_date() -> String {
     let now = LocalDateTime::now();
     format!("{}", now.date().iso())
 }
+
+/// Create the perfect hashing for file typing
+fn create_file_typing_hash_file() -> io::Result<()> {
+    let path = &PathBuf::from(env::var("OUT_DIR").unwrap()).join("filetype_maps.rs");
+    let mut file = io::BufWriter::new(File::create(path).unwrap_or_else(|_| {
+        panic!("{}", path.to_string_lossy().to_string())
+    }));
+    generate_filename_type_map(file.get_mut())?;
+    generate_extension_type_map(file.get_mut())?;
+    file.flush()
+}
+
+/// Create the perfect hashing for file icons
+fn create_file_icon_hash_file() -> io::Result<()> {
+    let path = &PathBuf::from(env::var("OUT_DIR").unwrap()).join("icon_maps.rs");
+    let mut file = io::BufWriter::new(File::create(path).unwrap_or_else(|_| {
+        panic!("{}", path.to_string_lossy().to_string());
+    }));
+    generate_filename_icon_map(file.get_mut())?;
+    generate_extension_icon_map(file.get_mut())?;
+    file.flush()
+}
+
+/// Generate mapping from full filenames to file type. For file types see info/filetype.rs
+fn generate_filename_type_map(file: &mut File) -> io::Result<()> {
+    writeln!(file, "static FILENAME_TYPES: phf::Map<&'static str, FileType> = {};\n",
+             phf_codegen::Map::new()
+                 /* Immediate file - kick off the build of a project */
+                 .entry("Brewfile",          "FileType::Immediate")
+                 .entry("bsconfig.json",     "FileType::Immediate")
+                 .entry("BUILD",             "FileType::Immediate")
+                 .entry("BUILD.bazel",       "FileType::Immediate")
+                 .entry("build.gradle",      "FileType::Immediate")
+                 .entry("build.sbt",         "FileType::Immediate")
+                 .entry("build.xml",         "FileType::Immediate")
+                 .entry("Cargo.lock",        "FileType::Immediate")
+                 .entry("Cargo.toml",        "FileType::Immediate")
+                 .entry("CMakeLists.txt",    "FileType::Immediate")
+                 .entry("composer.json",     "FileType::Immediate")
+                 .entry("configure.ac",      "FileType::Immediate")
+                 .entry("Configure.ac",      "FileType::Immediate")
+                 .entry("Containerfile",     "FileType::Immediate")
+                 .entry("Dockerfile",        "FileType::Immediate")
+                 .entry("Earthfile",         "FileType::Immediate")
+                 .entry("flake.lock",        "FileType::Immediate")
+                 .entry("flake.nix",         "FileType::Immediate")
+                 .entry("Gemfile",           "FileType::Immediate")
+                 .entry("GNUmakefile",       "FileType::Immediate")
+                 .entry("Gruntfile.coffee",  "FileType::Immediate")
+                 .entry("Gruntfile.js",      "FileType::Immediate")
+                 .entry("Justfile",          "FileType::Immediate")
+                 .entry("justfile",          "FileType::Immediate")
+                 .entry("Makefile",          "FileType::Immediate")
+                 .entry("makefile",          "FileType::Immediate")
+                 .entry("Makefile.in",       "FileType::Immediate")
+                 .entry("makefile.in",       "FileType::Immediate")
+                 .entry("meson.build",       "FileType::Immediate")
+                 .entry("mix.exs",           "FileType::Immediate")
+                 .entry("package.json",      "FileType::Immediate")
+                 .entry("Pipfile",           "FileType::Immediate")
+                 .entry("PKGBUILD",          "FileType::Immediate")
+                 .entry("Podfile",           "FileType::Immediate")
+                 .entry("pom.xml",           "FileType::Immediate")
+                 .entry("Procfile",          "FileType::Immediate")
+                 .entry("Rakefile",          "FileType::Immediate")
+                 .entry("RoboFile.php",      "FileType::Immediate")
+                 .entry("SConstruct",        "FileType::Immediate")
+                 .entry("tsconfig.json",     "FileType::Immediate")
+                 .entry("Vagrantfile",       "FileType::Immediate")
+                 .entry("webpack.config.cjs","FileType::Immediate")
+                 .entry("webpack.config.js", "FileType::Immediate")
+                 .entry("WORKSPACE",         "FileType::Immediate")
+                 .build()
+    )
+}
+
+/// Generate mapping from lowercase file extension to file type.  If an image, video, music, or
+/// lossless extension is added also update the extension icon map. For file types see
+/// info/filetype.rs
+fn generate_extension_type_map(file: &mut File) -> io::Result<()> {
+    // Extension are converted to lower case for comparison
+    writeln!(file, "static EXTENSION_TYPES: phf::Map<&'static str, FileType> = {};\n",
+             phf_codegen::Map::new()
+                 /* Immediate file - kick off the build of a project */
+                 .entry("ninja",     "FileType::Immediate")
+                 /* Image files */
+                 .entry("arw",       "FileType::Image")
+                 .entry("avif",      "FileType::Image")
+                 .entry("bmp",       "FileType::Image")
+                 .entry("cbr",       "FileType::Image")
+                 .entry("cbz",       "FileType::Image")
+                 .entry("cr2",       "FileType::Image")
+                 .entry("dvi",       "FileType::Image")
+                 .entry("eps",       "FileType::Image")
+                 .entry("gif",       "FileType::Image")
+                 .entry("heif",      "FileType::Image")
+                 .entry("ico",       "FileType::Image")
+                 .entry("j2c",       "FileType::Image")
+                 .entry("j2k",       "FileType::Image")
+                 .entry("jfi",       "FileType::Image")
+                 .entry("jfif",      "FileType::Image")
+                 .entry("jif",       "FileType::Image")
+                 .entry("jp2",       "FileType::Image")
+                 .entry("jpe",       "FileType::Image")
+                 .entry("jpeg",      "FileType::Image")
+                 .entry("jpf",       "FileType::Image")
+                 .entry("jpg",       "FileType::Image")
+                 .entry("jpx",       "FileType::Image")
+                 .entry("jxl",       "FileType::Image")
+                 .entry("nef",       "FileType::Image")
+                 .entry("orf",       "FileType::Image")
+                 .entry("pbm",       "FileType::Image")
+                 .entry("pgm",       "FileType::Image")
+                 .entry("png",       "FileType::Image")
+                 .entry("pnm",       "FileType::Image")
+                 .entry("ppm",       "FileType::Image")
+                 .entry("ps",        "FileType::Image")
+                 .entry("pxm",       "FileType::Image")
+                 .entry("raw",       "FileType::Image")
+                 .entry("stl",       "FileType::Image")
+                 .entry("svg",       "FileType::Image")
+                 .entry("tif",       "FileType::Image")
+                 .entry("tiff",      "FileType::Image")
+                 .entry("webp",      "FileType::Image")
+                 .entry("xpm",       "FileType::Image")
+                 /* Video files */
+                 .entry("avi",       "FileType::Video")
+                 .entry("flv",       "FileType::Video")
+                 .entry("heic",      "FileType::Video")
+                 .entry("m2ts",      "FileType::Video")
+                 .entry("m2v",       "FileType::Video")
+                 .entry("m4v",       "FileType::Video")
+                 .entry("mkv",       "FileType::Video")
+                 .entry("mov",       "FileType::Video")
+                 .entry("mp4",       "FileType::Video")
+                 .entry("mpeg",      "FileType::Video")
+                 .entry("mpg",       "FileType::Video")
+                 .entry("ogm",       "FileType::Video")
+                 .entry("ogv",       "FileType::Video")
+                 .entry("vob",       "FileType::Video")
+                 .entry("webm",      "FileType::Video")
+                 .entry("wmv",       "FileType::Video")
+                 /* Music files */
+                 .entry("aac",       "FileType::Music")
+                 .entry("m4a",       "FileType::Music")
+                 .entry("mka",       "FileType::Music")
+                 .entry("mp2",       "FileType::Music")
+                 .entry("mp3",       "FileType::Music")
+                 .entry("ogg",       "FileType::Music")
+                 .entry("opus",      "FileType::Music")
+                 .entry("wma",       "FileType::Music")
+                 /* Lossless music, rather than any other kind of data... */
+                 .entry("alac",      "FileType::Lossless")
+                 .entry("ape",       "FileType::Lossless")
+                 .entry("flac",      "FileType::Lossless")
+                 .entry("wav",       "FileType::Lossless")
+                 /* Cryptology files */
+                 .entry("asc",       "FileType::Crypto")
+                 .entry("enc",       "FileType::Crypto")
+                 .entry("gpg",       "FileType::Crypto")
+                 .entry("p12",       "FileType::Crypto")
+                 .entry("pfx",       "FileType::Crypto")
+                 .entry("pgp",       "FileType::Crypto")
+                 .entry("sig",       "FileType::Crypto")
+                 .entry("signature", "FileType::Crypto")
+                 /* Document files */
+                 .entry("djvu",      "FileType::Document")
+                 .entry("doc",       "FileType::Document")
+                 .entry("docx",      "FileType::Document")
+                 // .entry("dvi",       "FileType::Document") // already an image format
+                 .entry("eml",       "FileType::Document")
+                 // .entry("eps",       "FileType::Document") // already an image format
+                 .entry("fotd",      "FileType::Document")
+                 .entry("key",       "FileType::Document")
+                 .entry("keynote",   "FileType::Document")
+                 .entry("numbers",   "FileType::Document")
+                 .entry("odp",       "FileType::Document")
+                 .entry("odt",       "FileType::Document")
+                 .entry("pages",     "FileType::Document")
+                 .entry("pdf",       "FileType::Document")
+                 .entry("ppt",       "FileType::Document")
+                 .entry("pptx",      "FileType::Document")
+                 .entry("rtf",       "FileType::Document")
+                 .entry("xls",       "FileType::Document")
+                 .entry("xlsx",      "FileType::Document")
+                 /* Compressed/archive files */
+                 .entry("7z",        "FileType::Compressed")
+                 .entry("a",         "FileType::Compressed")
+                 .entry("ar",        "FileType::Compressed")
+                 .entry("bz",        "FileType::Compressed")
+                 .entry("bz2",       "FileType::Compressed")
+                 .entry("cpio",      "FileType::Compressed")
+                 .entry("deb",       "FileType::Compressed")
+                 .entry("dmg",       "FileType::Compressed")
+                 .entry("gz",        "FileType::Compressed")
+                 .entry("iso",       "FileType::Compressed")
+                 .entry("lz",        "FileType::Compressed")
+                 .entry("lz4",       "FileType::Compressed")
+                 .entry("lzh",       "FileType::Compressed")
+                 .entry("lzma",      "FileType::Compressed")
+                 .entry("lzo",       "FileType::Compressed")
+                 .entry("par",       "FileType::Compressed")
+                 .entry("rar",       "FileType::Compressed")
+                 .entry("rpm",       "FileType::Compressed")
+                 .entry("tar",       "FileType::Compressed")
+                 .entry("taz",       "FileType::Compressed")
+                 .entry("tbz",       "FileType::Compressed")
+                 .entry("tbz2",      "FileType::Compressed")
+                 .entry("tc",        "FileType::Compressed")
+                 .entry("tgz",       "FileType::Compressed")
+                 .entry("tlz",       "FileType::Compressed")
+                 .entry("txz",       "FileType::Compressed")
+                 .entry("tz",        "FileType::Compressed")
+                 .entry("tzo",       "FileType::Compressed")
+                 .entry("xz",        "FileType::Compressed")
+                 .entry("z",         "FileType::Compressed")
+                 .entry("zip",       "FileType::Compressed")
+                 .entry("zst",       "FileType::Compressed")
+                 /* Temporary files */
+                 .entry("bak",       "FileType::Temp")
+                 .entry("bk",        "FileType::Temp")
+                 .entry("bkp",       "FileType::Temp")
+                 .entry("swn",       "FileType::Temp")
+                 .entry("swo",       "FileType::Temp")
+                 .entry("swp",       "FileType::Temp")
+                 .entry("tmp",       "FileType::Temp")
+                 /* Compiler output files */
+                 .entry("class",     "FileType::Compiled")
+                 .entry("elc",       "FileType::Compiled")
+                 .entry("hi",        "FileType::Compiled")
+                 .entry("ko",        "FileType::Compiled")
+                 .entry("o",         "FileType::Compiled")
+                 .entry("pyc",       "FileType::Compiled")
+                 .entry("zwc",       "FileType::Compiled")
+                 .build()
+    )
+}
+
+/// Generate mapping from full filenames to file type. This mapping should also contain all the
+/// "dot" directories that have a custom icon.  See output/render/icons.rs for a partial list of
+/// icon constants.
+fn generate_filename_icon_map(file: &mut File) -> io::Result<()> {
+    writeln!(file, "static FILENAME_ICONS: phf::Map<&'static str, char> = {};\n",
+             phf_codegen::Map::new()
+                 .entry(".atom",              "'\u{e764}'") // 
+                 .entry(".bashprofile",       "'\u{e615}'") // 
+                 .entry(".bashrc",            "'\u{f489}'") // 
+                 .entry(".emacs",             "'\u{e632}'") // 
+                 .entry(".git",               "'\u{f1d3}'") // 
+                 .entry(".gitattributes",     "'\u{f1d3}'") // 
+                 .entry(".gitconfig",         "'\u{f1d3}'") // 
+                 .entry(".github",            "'\u{f408}'") // 
+                 .entry(".gitignore",         "'\u{f1d3}'") // 
+                 .entry(".gitignore_global",  "'\u{f1d3}'") // 
+                 .entry(".gitmodules",        "'\u{f1d3}'") // 
+                 .entry(".idea",              "'\u{e7b5}'") // 
+                 .entry(".rvm",               "'\u{e21e}'") // 
+                 .entry(".Trash",             "'\u{f1f8}'") // 
+                 .entry(".vimrc",             "'\u{e7c5}'") // 
+                 .entry(".vscode",            "'\u{e70c}'") // 
+                 .entry(".zshrc",             "'\u{f489}'") // 
+                 .entry("bin",                "'\u{e5fc}'") // 
+                 .entry("Cargo.lock",         "'\u{e7a8}'") // 
+                 .entry("config",             "'\u{e5fc}'") // 
+                 .entry("docker-compose.yml", "'\u{f308}'") // 
+                 .entry("Dockerfile",         "'\u{f308}'") // 
+                 .entry("ds_store",           "'\u{f179}'") // 
+                 .entry("Earthfile",          "'\u{f0ac}'") // 
+                 .entry("gitignore_global",   "'\u{f1d3}'") // 
+                 .entry("gitlab-ci.yml",      "'\u{f296}'") // 
+                 .entry("go.mod",             "'\u{e626}'") // 
+                 .entry("go.sum",             "'\u{e626}'") // 
+                 .entry("gradle",             "'\u{e256}'") // 
+                 .entry("gruntfile.coffee",   "'\u{e611}'") // 
+                 .entry("gruntfile.js",       "'\u{e611}'") // 
+                 .entry("gruntfile.ls",       "'\u{e611}'") // 
+                 .entry("gulpfile.coffee",    "'\u{e610}'") // 
+                 .entry("gulpfile.js",        "'\u{e610}'") // 
+                 .entry("gulpfile.ls",        "'\u{e610}'") // 
+                 .entry("hidden",             "'\u{f023}'") // 
+                 .entry("include",            "'\u{e5fc}'") // 
+                 .entry("lib",                "'\u{f121}'") // 
+                 .entry("LICENSE",            "'\u{f02d}'") // 
+                 .entry("localized",          "'\u{f179}'") // 
+                 .entry("Makefile",           "'\u{f489}'") // 
+                 .entry("node_modules",       "'\u{e718}'") // 
+                 .entry("npmignore",          "'\u{e71e}'") // 
+                 .entry("PKGBUILD",           "'\u{f303}'") // 
+                 .entry("rubydoc",            "'\u{e73b}'") // 
+                 .entry("Vagrantfile",        "'\u{2371}'") // ⍱
+                 .entry("yarn.lock",          "'\u{e718}'") // 
+                 .build()
+    )
+}
+
+/// Generate mapping from lowercase file extension to icons.  If an image, video, or audio
+/// extension is add also update the extension filetype map.  See output/render/icons.rs for
+/// a partial list of icon constants.
+fn generate_extension_icon_map(file: &mut File) -> io::Result<()> {
+    writeln!(file, "static EXTENSION_ICONS: phf::Map<&'static str, char> = {};\n",
+             phf_codegen::Map::new()
+                 .entry("7z",            "'\u{f410}'")  // 
+                 .entry("a",             "'\u{f17c}'")  // 
+                 .entry("acc",           "'\u{f001}'")  // 
+                 .entry("acf",           "'\u{f1b6}'")  // 
+                 .entry("ai",            "'\u{e7b4}'")  // 
+                 .entry("alac",          "'\u{f001}'")  // 
+                 .entry("android",       "'\u{e70e}'")  // 
+                 .entry("ape",           "'\u{f001}'")  // 
+                 .entry("apk",           "'\u{e70e}'")  // 
+                 .entry("apple",         "'\u{f179}'")  // 
+                 .entry("ar",            "'\u{f410}'")  // 
+                 .entry("arw",           "'\u{f1c5}'")  // 
+                 .entry("asm",           "'\u{e637}'")  // 
+                 .entry("avi",           "'\u{f03d}'")  // 
+                 .entry("avif",          "'\u{f1c5}'")  // 
+                 .entry("avro",          "'\u{e60b}'")  // 
+                 .entry("awk",           "'\u{f489}'")  // 
+                 .entry("bash",          "'\u{f489}'")  // 
+                 .entry("bashrc",        "'\u{f489}'")  // 
+                 .entry("bash_history",  "'\u{f489}'")  // 
+                 .entry("bash_profile",  "'\u{f489}'")  // 
+                 .entry("bat",           "'\u{ebc4}'")  // 
+                 .entry("bats",          "'\u{f489}'")  // 
+                 .entry("bib",           "'\u{e69b}'")  // 
+                 .entry("bin",           "'\u{eae8}'")  // 
+                 .entry("bmp",           "'\u{f1c5}'")  // 
+                 .entry("bst",           "'\u{e69b}'")  // 
+                 .entry("bz",            "'\u{f410}'")  // 
+                 .entry("bz2",           "'\u{f410}'")  // 
+                 .entry("c",             "'\u{e61e}'")  // 
+                 .entry("c++",           "'\u{e61d}'")  // 
+                 .entry("cab",           "'\u{e70f}'")  // 
+                 .entry("cbr",           "'\u{f1c5}'")  // 
+                 .entry("cbz",           "'\u{f1c5}'")  // 
+                 .entry("cc",            "'\u{e61d}'")  // 
+                 .entry("cert",          "'\u{eafa}'")  // 
+                 .entry("cfg",           "'\u{e615}'")  // 
+                 .entry("cjs",           "'\u{e74e}'")  // 
+                 .entry("class",         "'\u{e256}'")  // 
+                 .entry("clj",           "'\u{e768}'")  // 
+                 .entry("cljs",          "'\u{e76a}'")  // 
+                 .entry("cls",           "'\u{e69b}'")  // 
+                 .entry("cmd",           "'\u{e70f}'")  // 
+                 .entry("coffee",        "'\u{f0f4}'")  // 
+                 .entry("conf",          "'\u{e615}'")  // 
+                 .entry("config",        "'\u{e615}'")  // 
+                 .entry("cp",            "'\u{e61d}'")  // 
+                 .entry("cpio",          "'\u{f410}'")  // 
+                 .entry("cpp",           "'\u{e61d}'")  // 
+                 .entry("cr2",           "'\u{f1c5}'")  // 
+                 .entry("crt",           "'\u{eafa}'")  // 
+                 .entry("cs",            "'\u{f031b}'") // 󰌛
+                 .entry("csh",           "'\u{f489}'")  // 
+                 .entry("cshtml",        "'\u{f1fa}'")  // 
+                 .entry("csproj",        "'\u{f031b}'") // 󰌛
+                 .entry("css",           "'\u{e749}'")  // 
+                 .entry("csv",           "'\u{f1c3}'")  // 
+                 .entry("csx",           "'\u{f031b}'") // 󰌛
+                 .entry("cts",           "'\u{e628}'")  // 
+                 .entry("cu",            "'\u{e64b}'")  // 
+                 .entry("cxx",           "'\u{e61d}'")  // 
+                 .entry("d",             "'\u{e7af}'")  // 
+                 .entry("dart",          "'\u{e798}'")  // 
+                 .entry("db",            "'\u{f1c0}'")  // 
+                 .entry("deb",           "'\u{e77d}'")  // 
+                 .entry("desktop",       "'\u{ebd1}'")  // 
+                 .entry("diff",          "'\u{f440}'")  // 
+                 .entry("djvu",          "'\u{f02d}'")  // 
+                 .entry("dll",           "'\u{e70f}'")  // 
+                 .entry("dmg",           "'\u{e271}'")  // 
+                 .entry("doc",           "'\u{f1c2}'")  // 
+                 .entry("docx",          "'\u{f1c2}'")  // 
+                 .entry("drawio",        "'\u{ebba}'")  // 
+                 .entry("ds_store",      "'\u{f179}'")  // 
+                 .entry("dump",          "'\u{f1c0}'")  // 
+                 .entry("dvi",           "'\u{f1c5}'")  // 
+                 .entry("ebook",         "'\u{e28b}'")  // 
+                 .entry("ebuild",        "'\u{f30d}'")  // 
+                 .entry("editorconfig",  "'\u{e615}'")  // 
+                 .entry("ejs",           "'\u{e618}'")  // 
+                 .entry("el",            "'\u{e632}'")  // 
+                 .entry("elm",           "'\u{e62c}'")  // 
+                 .entry("eml",           "'\u{f003}'")  // 
+                 .entry("env",           "'\u{f462}'")  // 
+                 .entry("eot",           "'\u{f031}'")  // 
+                 .entry("eps",           "'\u{f1c5}'")  // 
+                 .entry("epub",          "'\u{e28a}'")  // 
+                 .entry("erb",           "'\u{e73b}'")  // 
+                 .entry("erl",           "'\u{e7b1}'")  // 
+                 .entry("ex",            "'\u{e62d}'")  // 
+                 .entry("exe",           "'\u{f17a}'")  // 
+                 .entry("exs",           "'\u{e62d}'")  // 
+                 .entry("fish",          "'\u{f489}'")  // 
+                 .entry("flac",          "'\u{f001}'")  // 
+                 .entry("flv",           "'\u{f03d}'")  // 
+                 .entry("font",          "'\u{f031}'")  // 
+                 .entry("fs",            "'\u{e7a7}'")  // 
+                 .entry("fsi",           "'\u{e7a7}'")  // 
+                 .entry("fsx",           "'\u{e7a7}'")  // 
+                 .entry("gdoc",          "'\u{f1c2}'")  // 
+                 .entry("gem",           "'\u{e21e}'")  // 
+                 .entry("gemfile",       "'\u{e21e}'")  // 
+                 .entry("gemspec",       "'\u{e21e}'")  // 
+                 .entry("gform",         "'\u{f298}'")  // 
+                 .entry("gif",           "'\u{f1c5}'")  // 
+                 .entry("git",           "'\u{f1d3}'")  // 
+                 .entry("gitattributes", "'\u{f1d3}'")  // 
+                 .entry("gitignore",     "'\u{f1d3}'")  // 
+                 .entry("gitmodules",    "'\u{f1d3}'")  // 
+                 .entry("go",            "'\u{e626}'")  // 
+                 .entry("gpg",           "'\u{e60a}'")  // 
+                 .entry("gradle",        "'\u{e256}'")  // 
+                 .entry("groovy",        "'\u{e775}'")  // 
+                 .entry("gsheet",        "'\u{f1c3}'")  // 
+                 .entry("gslides",       "'\u{f1c4}'")  // 
+                 .entry("guardfile",     "'\u{e21e}'")  // 
+                 .entry("gz",            "'\u{f410}'")  // 
+                 .entry("h",             "'\u{f0fd}'")  // 
+                 .entry("hbs",           "'\u{e60f}'")  // 
+                 .entry("heic",          "'\u{f03d}'")  // 
+                 .entry("heif",          "'\u{f1c5}'")  // 
+                 .entry("hpp",           "'\u{f0fd}'")  // 
+                 .entry("hs",            "'\u{e777}'")  // 
+                 .entry("htm",           "'\u{f13b}'")  // 
+                 .entry("html",          "'\u{f13b}'")  // 
+                 .entry("hxx",           "'\u{f0fd}'")  // 
+                 .entry("ical",          "'\u{eab0}'")  // 
+                 .entry("icalendar",     "'\u{eab0}'")  // 
+                 .entry("ico",           "'\u{f1c5}'")  // 
+                 .entry("ics",           "'\u{eab0}'")  // 
+                 .entry("ifb",           "'\u{eab0}'")  // 
+                 .entry("image",         "'\u{f1c5}'")  // 
+                 .entry("img",           "'\u{e271}'")  // 
+                 .entry("iml",           "'\u{e7b5}'")  // 
+                 .entry("ini",           "'\u{f17a}'")  // 
+                 .entry("ipynb",         "'\u{e678}'")  // 
+                 .entry("iso",           "'\u{e271}'")  // 
+                 .entry("j2c",           "'\u{f1c5}'")  // 
+                 .entry("j2k",           "'\u{f1c5}'")  // 
+                 .entry("jad",           "'\u{e256}'")  // 
+                 .entry("jar",           "'\u{e256}'")  // 
+                 .entry("java",          "'\u{e256}'")  // 
+                 .entry("jfi",           "'\u{f1c5}'")  // 
+                 .entry("jfif",          "'\u{f1c5}'")  // 
+                 .entry("jif",           "'\u{f1c5}'")  // 
+                 .entry("jl",            "'\u{e624}'")  // 
+                 .entry("jmd",           "'\u{f48a}'")  // 
+                 .entry("jp2",           "'\u{f1c5}'")  // 
+                 .entry("jpe",           "'\u{f1c5}'")  // 
+                 .entry("jpeg",          "'\u{f1c5}'")  // 
+                 .entry("jpf",           "'\u{f1c5}'")  // 
+                 .entry("jpg",           "'\u{f1c5}'")  // 
+                 .entry("jpx",           "'\u{f1c5}'")  // 
+                 .entry("js",            "'\u{e74e}'")  // 
+                 .entry("json",          "'\u{e60b}'")  // 
+                 .entry("jsx",           "'\u{e7ba}'")  // 
+                 .entry("jxl",           "'\u{f1c5}'")  // 
+                 .entry("kdb",           "'\u{f23e}'")  // 
+                 .entry("kdbx",          "'\u{f23e}'")  // 
+                 .entry("key",           "'\u{eb11}'")  // 
+                 .entry("ko",            "'\u{f17c}'")  // 
+                 .entry("ksh",           "'\u{f489}'")  // 
+                 .entry("latex",         "'\u{e69b}'")  // 
+                 .entry("less",          "'\u{e758}'")  // 
+                 .entry("lhs",           "'\u{e777}'")  // 
+                 .entry("license",       "'\u{f02d}'")  // 
+                 .entry("localized",     "'\u{f179}'")  // 
+                 .entry("lock",          "'\u{f023}'")  // 
+                 .entry("log",           "'\u{f18d}'")  // 
+                 .entry("lua",           "'\u{e620}'")  // 
+                 .entry("lz",            "'\u{f410}'")  // 
+                 .entry("lz4",           "'\u{f410}'")  // 
+                 .entry("lzh",           "'\u{f410}'")  // 
+                 .entry("lzma",          "'\u{f410}'")  // 
+                 .entry("lzo",           "'\u{f410}'")  // 
+                 .entry("m",             "'\u{e61e}'")  // 
+                 .entry("m2ts",          "'\u{f03d}'")  // 
+                 .entry("m2v",           "'\u{f03d}'")  // 
+                 .entry("m4a",           "'\u{f001}'")  // 
+                 .entry("m4v",           "'\u{f03d}'")  // 
+                 .entry("magnet",        "'\u{f076}'")  // 
+                 .entry("markdown",      "'\u{f48a}'")  // 
+                 .entry("md",            "'\u{f48a}'")  // 
+                 .entry("mjs",           "'\u{e74e}'")  // 
+                 .entry("mk",            "'\u{f489}'")  // 
+                 .entry("mka",           "'\u{f001}'")  // 
+                 .entry("mkd",           "'\u{f48a}'")  // 
+                 .entry("mkv",           "'\u{f03d}'")  // 
+                 .entry("ml",            "'\u{e67a}'")  // 
+                 .entry("mli",           "'\u{e67a}'")  // 
+                 .entry("mll",           "'\u{e67a}'")  // 
+                 .entry("mly",           "'\u{e67a}'")  // 
+                 .entry("mm",            "'\u{e61d}'")  // 
+                 .entry("mobi",          "'\u{e28b}'")  // 
+                 .entry("mov",           "'\u{f03d}'")  // 
+                 .entry("mp2",           "'\u{f001}'")  // 
+                 .entry("mp3",           "'\u{f001}'")  // 
+                 .entry("mp4",           "'\u{f03d}'")  // 
+                 .entry("mpeg",          "'\u{f03d}'")  // 
+                 .entry("mpg",           "'\u{f03d}'")  // 
+                 .entry("msi",           "'\u{e70f}'")  // 
+                 .entry("mts",           "'\u{e628}'")  // 
+                 .entry("mustache",      "'\u{e60f}'")  // 
+                 .entry("nef",           "'\u{f1c5}'")  // 
+                 .entry("ninja",         "'\u{f0774}'") // 󰝴
+                 .entry("nix",           "'\u{f313}'")  // 
+                 .entry("node",          "'\u{f0399}'") // 󰎙
+                 .entry("npmignore",     "'\u{e71e}'")  // 
+                 .entry("o",             "'\u{eae8}'")  // 
+                 .entry("odp",           "'\u{f1c4}'")  // 
+                 .entry("ods",           "'\u{f1c3}'")  // 
+                 .entry("odt",           "'\u{f1c2}'")  // 
+                 .entry("ogg",           "'\u{f001}'")  // 
+                 .entry("ogm",           "'\u{f03d}'")  // 
+                 .entry("ogv",           "'\u{f03d}'")  // 
+                 .entry("opus",          "'\u{f001}'")  // 
+                 .entry("orf",           "'\u{f1c5}'")  // 
+                 .entry("org",           "'\u{e633}'")  // 
+                 .entry("otf",           "'\u{f031}'")  // 
+                 .entry("out",           "'\u{eb2c}'")  // 
+                 .entry("par",           "'\u{f410}'")  // 
+                 .entry("part",          "'\u{f43a}'")  // 
+                 .entry("patch",         "'\u{f440}'")  // 
+                 .entry("pbm",           "'\u{f1c5}'")  // 
+                 .entry("pdf",           "'\u{f1c1}'")  // 
+                 .entry("pem",           "'\u{eb11}'")  // 
+                 .entry("pgm",           "'\u{f1c5}'")  // 
+                 .entry("php",           "'\u{e73d}'")  // 
+                 .entry("pl",            "'\u{e769}'")  // 
+                 .entry("plx",           "'\u{e769}'")  // 
+                 .entry("pm",            "'\u{e769}'")  // 
+                 .entry("png",           "'\u{f1c5}'")  // 
+                 .entry("pnm",           "'\u{f1c5}'")  // 
+                 .entry("pod",           "'\u{e769}'")  // 
+                 .entry("ppm",           "'\u{f1c5}'")  // 
+                 .entry("ppt",           "'\u{f1c4}'")  // 
+                 .entry("pptx",          "'\u{f1c4}'")  // 
+                 .entry("procfile",      "'\u{e21e}'")  // 
+                 .entry("properties",    "'\u{e60b}'")  // 
+                 .entry("ps",            "'\u{f1c5}'")  // 
+                 .entry("ps1",           "'\u{ebc7}'")  // 
+                 .entry("psd",           "'\u{e7b8}'")  // 
+                 .entry("psd1",          "'\u{ebc7}'")  // 
+                 .entry("psm1",          "'\u{ebc7}'")  // 
+                 .entry("pxm",           "'\u{f1c5}'")  // 
+                 .entry("py",            "'\u{e606}'")  // 
+                 .entry("pyc",           "'\u{e606}'")  // 
+                 .entry("qcow2",         "'\u{e271}'")  // 
+                 .entry("r",             "'\u{f25d}'")  // 
+                 .entry("rakefile",      "'\u{e21e}'")  // 
+                 .entry("rar",           "'\u{f410}'")  // 
+                 .entry("raw",           "'\u{f1c5}'")  // 
+                 .entry("razor",         "'\u{f1fa}'")  // 
+                 .entry("rb",            "'\u{e21e}'")  // 
+                 .entry("rdata",         "'\u{f25d}'")  // 
+                 .entry("rdb",           "'\u{e76d}'")  // 
+                 .entry("rdoc",          "'\u{f48a}'")  // 
+                 .entry("rds",           "'\u{f25d}'")  // 
+                 .entry("readme",        "'\u{f48a}'")  // 
+                 .entry("rlib",          "'\u{e7a8}'")  // 
+                 .entry("rmd",           "'\u{f48a}'")  // 
+                 .entry("rmeta",         "'\u{e7a8}'")  // 
+                 .entry("rpm",           "'\u{e7bb}'")  // 
+                 .entry("rs",            "'\u{e7a8}'")  // 
+                 .entry("rspec",         "'\u{e21e}'")  // 
+                 .entry("rspec_parallel","'\u{e21e}'")  // 
+                 .entry("rspec_status",  "'\u{e21e}'")  // 
+                 .entry("rss",           "'\u{f09e}'")  // 
+                 .entry("rst",           "'\u{f15c}'")  // 
+                 .entry("rtf",           "'\u{f0219}'") // 󰈙
+                 .entry("ru",            "'\u{e21e}'")  // 
+                 .entry("rubydoc",       "'\u{e73b}'")  // 
+                 .entry("s",             "'\u{e637}'")  // 
+                 .entry("sass",          "'\u{e603}'")  // 
+                 .entry("scala",         "'\u{e737}'")  // 
+                 .entry("scss",          "'\u{e749}'")  // 
+                 .entry("service",       "'\u{eba2}'")  // 
+                 .entry("sh",            "'\u{f489}'")  // 
+                 .entry("shell",         "'\u{f489}'")  // 
+                 .entry("slim",          "'\u{e73b}'")  // 
+                 .entry("sln",           "'\u{e70c}'")  // 
+                 .entry("so",            "'\u{f17c}'")  // 
+                 .entry("sql",           "'\u{f1c0}'")  // 
+                 .entry("sqlite3",       "'\u{e7c4}'")  // 
+                 .entry("stl",           "'\u{f1c5}'")  // 
+                 .entry("sty",           "'\u{e69b}'")  // 
+                 .entry("styl",          "'\u{e600}'")  // 
+                 .entry("stylus",        "'\u{e600}'")  // 
+                 .entry("svelte",        "'\u{e697}'")  // 
+                 .entry("svg",           "'\u{f1c5}'")  // 
+                 .entry("swift",         "'\u{e755}'")  // 
+                 .entry("t",             "'\u{e769}'")  // 
+                 .entry("tar",           "'\u{f410}'")  // 
+                 .entry("taz",           "'\u{f410}'")  // 
+                 .entry("tbz",           "'\u{f410}'")  // 
+                 .entry("tbz2",          "'\u{f410}'")  // 
+                 .entry("tc",            "'\u{f410}'")  // 
+                 .entry("tex",           "'\u{e69b}'")  // 
+                 .entry("tgz",           "'\u{f410}'")  // 
+                 .entry("tif",           "'\u{f1c5}'")  // 
+                 .entry("tiff",          "'\u{f1c5}'")  // 
+                 .entry("tlz",           "'\u{f410}'")  // 
+                 .entry("toml",          "'\u{e615}'")  // 
+                 .entry("torrent",       "'\u{e275}'")  // 
+                 .entry("ts",            "'\u{e628}'")  // 
+                 .entry("tsv",           "'\u{f1c3}'")  // 
+                 .entry("tsx",           "'\u{e7ba}'")  // 
+                 .entry("ttf",           "'\u{f031}'")  // 
+                 .entry("twig",          "'\u{e61c}'")  // 
+                 .entry("txt",           "'\u{f15c}'")  // 
+                 .entry("txz",           "'\u{f410}'")  // 
+                 .entry("tz",            "'\u{f410}'")  // 
+                 .entry("tzo",           "'\u{f410}'")  // 
+                 .entry("unity",         "'\u{e721}'")  // 
+                 .entry("unity3d",       "'\u{e721}'")  // 
+                 .entry("vdi",           "'\u{e271}'")  // 
+                 .entry("vhd",           "'\u{e271}'")  // 
+                 .entry("video",         "'\u{f03d}'")  // 
+                 .entry("vim",           "'\u{e7c5}'")  // 
+                 .entry("vmdk",          "'\u{e271}'")  // 
+                 .entry("vob",           "'\u{f03d}'")  // 
+                 .entry("vue",           "'\u{f0844}'") // 󰡄
+                 .entry("war",           "'\u{e256}'")  // 
+                 .entry("wav",           "'\u{f001}'")  // 
+                 .entry("webm",          "'\u{f03d}'")  // 
+                 .entry("webp",          "'\u{f1c5}'")  // 
+                 .entry("windows",       "'\u{f17a}'")  // 
+                 .entry("wma",           "'\u{f001}'")  // 
+                 .entry("wmv",           "'\u{f03d}'")  // 
+                 .entry("woff",          "'\u{f031}'")  // 
+                 .entry("woff2",         "'\u{f031}'")  // 
+                 .entry("xhtml",         "'\u{f13b}'")  // 
+                 .entry("xls",           "'\u{f1c3}'")  // 
+                 .entry("xlsm",          "'\u{f1c3}'")  // 
+                 .entry("xlsx",          "'\u{f1c3}'")  // 
+                 .entry("xml",           "'\u{f05c0}'") // 󰗀
+                 .entry("xpm",           "'\u{f1c5}'")  // 
+                 .entry("xul",           "'\u{f05c0}'") // 󰗀
+                 .entry("xz",            "'\u{f410}'")  // 
+                 .entry("yaml",          "'\u{f481}'")  // 
+                 .entry("yml",           "'\u{f481}'")  // 
+                 .entry("z",             "'\u{f410}'")  // 
+                 .entry("zig",           "'\u{21af}'")  // ↯
+                 .entry("zip",           "'\u{f410}'")  // 
+                 .entry("zsh",           "'\u{f489}'")  // 
+                 .entry("zsh-theme",     "'\u{f489}'")  // 
+                 .entry("zshrc",         "'\u{f489}'")  // 
+                 .entry("zst",           "'\u{f410}'")  // 
+                 .build()
+    )
+}

+ 0 - 16
src/fs/file.rs

@@ -662,22 +662,6 @@ impl<'dir> File<'dir> {
         }
     }
 
-    /// Whether this file’s extension is any of the strings that get passed in.
-    ///
-    /// This will always return `false` if the file has no extension.
-    pub fn extension_is_one_of(&self, choices: &[&str]) -> bool {
-        match &self.ext {
-            Some(ext)  => choices.contains(&&ext[..]),
-            None       => false,
-        }
-    }
-
-    /// Whether this file’s name, including extension, is any of the strings
-    /// that get passed in.
-    pub fn name_is_one_of(&self, choices: &[&str]) -> bool {
-        choices.contains(&&self.name[..])
-    }
-
     /// This file’s security context field.
     pub fn security_context(&self) -> f::SecurityContext<'_> {
         let context = match &self.extended_attributes.iter().find(|a| a.name == "security.selinux") {

+ 49 - 285
src/info/filetype.rs

@@ -8,269 +8,55 @@
 //! Please keep these lists sorted. If you're using vim, :sort i
 
 use ansi_term::Style;
+use phf;
 
 use crate::fs::File;
-use crate::output::icons::FileIcon;
 use crate::theme::FileColours;
 
+#[derive(Debug, Clone)]
+enum FileType {
+    Image,
+    Video,
+    Music,
+    Lossless, // Lossless music, rather than any other kind of data...
+    Crypto,
+    Document,
+    Compressed,
+    Temp,
+    Compiled,
+    // An “immediate” file is something that can be run or activated somehow
+    // in order to kick off the build of a project. It’s usually only present
+    // in directories full of source code.
+    Immediate
+}
+
+// See build.rs for EXTENSION_TYPES and FILENAME_TYPES
+include!(concat!(env!("OUT_DIR"), "/filetype_maps.rs"));
 
 #[derive(Debug, Default, PartialEq, Eq)]
 pub struct FileExtensions;
 
 impl FileExtensions {
-
-    /// An “immediate” file is something that can be run or activated somehow
-    /// in order to kick off the build of a project. It’s usually only present
-    /// in directories full of source code.
-    #[allow(clippy::case_sensitive_file_extension_comparisons)]
-    fn is_immediate(&self, file: &File<'_>) -> bool {
-	file.name.to_lowercase().starts_with("readme")
-            || file.name.ends_with(".ninja")
-            || matches!(
-                file.name.as_str(),
-                "BUILD"
-                    | "Brewfile"
-                    | "bsconfig.json"
-                    | "BUILD.bazel"
-                    | "build.gradle"
-                    | "build.sbt"
-                    | "build.xml"
-                    | "Cargo.lock"
-                    | "Cargo.toml"
-                    | "CMakeLists.txt"
-                    | "composer.json"
-                    | "configure.ac"
-                    | "Configure.ac"
-                    | "Containerfile"
-                    | "Dockerfile"
-                    | "Earthfile"
-                    | "flake.lock"
-                    | "flake.nix"
-                    | "Gemfile"
-                    | "GNUmakefile"
-                    | "Gruntfile.coffee"
-                    | "Gruntfile.js"
-                    | "Justfile"
-                    | "justfile"
-                    | "Makefile"
-                    | "makefile"
-                    | "Makefile.in"
-                    | "makefile.in"
-                    | "meson.build"
-                    | "mix.exs"
-                    | "package.json"
-                    | "Pipfile"
-                    | "PKGBUILD"
-                    | "Podfile"
-                    | "pom.xml"
-                    | "Procfile"
-                    | "Rakefile"
-                    | "RoboFile.php"
-                    | "SConstruct"
-                    | "tsconfig.json"
-                    | "Vagrantfile"
-                    | "webpack.config.cjs"
-                    | "webpack.config.js"
-                    | "WORKSPACE"
-            )
-    }
-
-    fn is_image(&self, file: &File<'_>) -> bool {
-        file.extension_is_one_of( &[
-            "arw",
-            "avif",
-            "bmp",
-            "cbr",
-            "cbz",
-            "cr2",
-            "dvi",
-            "eps",
-            "gif",
-            "heif",
-            "ico",
-            "j2c",
-            "j2k",
-            "jfi",
-            "jfif",
-            "jif",
-            "jp2",
-            "jpe",
-            "jpeg",
-            "jpf",
-            "jpg",
-            "jpx",
-            "jxl",
-            "nef",
-            "orf",
-            "pbm",
-            "pgm",
-            "png",
-            "pnm",
-            "ppm",
-            "ps",
-            "pxm",
-            "raw",
-            "stl",
-            "svg",
-            "tif",
-            "tiff",
-            "webp",
-            "xpm",
-        ])
-    }
-
-    fn is_video(&self, file: &File<'_>) -> bool {
-        file.extension_is_one_of( &[
-            "avi",
-            "flv",
-            "heic",
-            "m2ts",
-            "m2v",
-            "m4v",
-            "mkv",
-            "mov",
-            "mp4",
-            "mpeg",
-            "mpg",
-            "ogm",
-            "ogv",
-            "vob",
-            "webm",
-            "wmv",
-        ])
-    }
-
-    fn is_music(&self, file: &File<'_>) -> bool {
-        file.extension_is_one_of( &[
-            "aac",
-            "m4a",
-            "mka",
-            "mp2",
-            "mp3",
-            "ogg",
-            "opus",
-            "wma",
-        ])
-    }
-
-    // Lossless music, rather than any other kind of data...
-    fn is_lossless(&self, file: &File<'_>) -> bool {
-        file.extension_is_one_of( &[
-            "alac",
-            "ape",
-            "flac",
-            "wav",
-        ])
-    }
-
-    fn is_crypto(&self, file: &File<'_>) -> bool {
-        file.extension_is_one_of( &[
-            "asc",
-            "enc",
-            "gpg",
-            "p12",
-            "pfx",
-            "pgp",
-            "sig",
-            "signature",
-        ])
-    }
-
-    fn is_document(&self, file: &File<'_>) -> bool {
-        file.extension_is_one_of( &[
-            "djvu",
-            "doc",
-            "docx",
-            "dvi",
-            "eml",
-            "eps",
-            "fotd",
-            "key",
-            "keynote",
-            "numbers",
-            "odp",
-            "odt",
-            "pages",
-            "pdf",
-            "ppt",
-            "pptx",
-            "rtf",
-            "xls",
-            "xlsx",
-        ])
-    }
-
-    fn is_compressed(&self, file: &File<'_>) -> bool {
-        file.extension_is_one_of( &[
-            "7z",
-            "a",
-            "ar",
-            "bz",
-            "bz2",
-            "bz3",
-            "cpio",
-            "deb",
-            "dmg",
-            "gz",
-            "iso",
-            "lz",
-            "lz4",
-            "lzh",
-            "lzma",
-            "lzo",
-            "par",
-            "rar",
-            "rpm",
-            "tar",
-            "taz",
-            "tbz",
-            "tbz2",
-            "tc",
-            "tgz",
-            "tlz",
-            "txz",
-            "tz",
-            "tzo",
-            "xz",
-            "Z",
-            "z",
-            "zip",
-            "zst",
-        ])
-    }
-
-    fn is_temp(&self, file: &File<'_>) -> bool {
-        file.name.ends_with('~')
-            || (file.name.starts_with('#') && file.name.ends_with('#'))
-            || file.extension_is_one_of( &[
-                "bak",
-                "bk",
-                "bkp",
-                "swn",
-                "swo",
-                "swp",
-                "tmp",
-            ])
-    }
-
-    fn is_compiled(&self, file: &File<'_>) -> bool {
-        if file.extension_is_one_of( &[
-            "class",
-            "elc",
-            "hi",
-            "ko",
-            "o",
-            "pyc",
-            "zwc",
-        ]) {
-            true
+    fn get_file_type(file: &File<'_>) -> Option<FileType> {
+        // Case-insensitive readme is checked first for backwards compatibility.
+        if file.name.to_lowercase().starts_with("readme") {
+            return Some(FileType::Immediate)
+        }
+        if let Some(file_type) = FILENAME_TYPES.get(&file.name) {
+            return Some(file_type.clone())
         }
-        else if let Some(dir) = file.parent_dir {
-            file.get_source_files().iter().any(|path| dir.contains(path))
+        if let Some(file_type) = file.ext.as_ref().and_then(|ext| EXTENSION_TYPES.get(ext)) {
+            return Some(file_type.clone())
         }
-        else {
-            false
+        if file.name.ends_with('~') || (file.name.starts_with('#') && file.name.ends_with('#')) {
+            return Some(FileType::Temp)
         }
+        if let Some(dir) = file.parent_dir {
+            if file.get_source_files().iter().any(|path| dir.contains(path)) {
+                return Some(FileType::Compiled)
+            }
+        }
+        None
     }
 }
 
@@ -278,40 +64,18 @@ impl FileColours for FileExtensions {
     fn colour_file(&self, file: &File<'_>) -> Option<Style> {
         use ansi_term::Colour::*;
 
-        Some(match file {
-            f if self.is_compiled(f)    => Yellow.normal(),
-            f if self.is_compressed(f)  => Red.normal(),
-            f if self.is_crypto(f)      => Green.bold(),
-            f if self.is_document(f)    => Green.normal(),
-            f if self.is_image(f)       => Purple.normal(),
-            f if self.is_immediate(f)   => Yellow.bold().underline(),
-            f if self.is_lossless(f)    => Cyan.bold(),
-            f if self.is_music(f)       => Cyan.normal(),
-            f if self.is_temp(f)        => White.normal(),
-            f if self.is_video(f)       => Purple.bold(),
-            _                           => return None,
-        })
-    }
-}
-
-impl FileIcon for FileExtensions {
-    fn icon_file(&self, file: &File<'_>) -> Option<char> {
-        use crate::output::icons::Icons;
-
-        if self.is_music(file) || self.is_lossless(file) {
-            Some(Icons::Audio.value())
-        }
-        else if self.is_image(file) {
-            Some(Icons::Image.value())
-        }
-        else if self.is_video(file) {
-            Some(Icons::Video.value())
-        }
-        else if self.is_compressed(file) {
-            Some(Icons::Compressed.value())
-        }
-        else {
-            None
+        match FileExtensions::get_file_type(file) {
+            Some(FileType::Compiled)   => Some(Yellow.normal()),
+            Some(FileType::Compressed) => Some(Red.normal()),
+            Some(FileType::Crypto)     => Some(Green.bold()),
+            Some(FileType::Document)   => Some(Green.normal()),
+            Some(FileType::Image)      => Some(Purple.normal()),
+            Some(FileType::Immediate)  => Some(Yellow.bold().underline()),
+            Some(FileType::Lossless)   => Some(Cyan.bold()),
+            Some(FileType::Music)      => Some(Cyan.normal()),
+            Some(FileType::Temp)       => Some(White.normal()),
+            Some(FileType::Video)      => Some(Purple.bold()),
+            _                          => None
         }
     }
 }

+ 13 - 414
src/output/icons.rs

@@ -1,35 +1,10 @@
 use ansi_term::Style;
+use phf;
 
 use crate::fs::File;
-use crate::info::filetype::FileExtensions;
-use lazy_static::lazy_static;
-use std::collections::HashMap;
-
-
-pub trait FileIcon {
-    fn icon_file(&self, file: &File<'_>) -> Option<char>;
-}
-
-
-#[derive(Copy, Clone)]
-pub enum Icons {
-    Audio,
-    Image,
-    Video,
-    Compressed,
-}
-
-impl Icons {
-    pub fn value(self) -> char {
-        match self {
-            Self::Audio  => '\u{f001}', // 
-            Self::Image  => '\u{f1c5}', // 
-            Self::Video  => '\u{f03d}', // 
-            Self::Compressed => '\u{f410}', // 
-        }
-    }
-}
 
+// See build.rs for FILENAME_ICONS and EXTENSION_ICONS
+include!(concat!(env!("OUT_DIR"), "/icon_maps.rs"));
 
 /// Converts the style used to paint a file name into the style that should be
 /// used to paint an icon.
@@ -45,394 +20,18 @@ pub fn iconify_style(style: Style) -> Style {
          .unwrap_or_default()
 }
 
-
-
-lazy_static! {
-    static ref MAP_BY_NAME: HashMap<&'static str, char> = {
-        let mut m = HashMap::new();
-        m.insert(".Trash", '\u{f1f8}'); // 
-        m.insert(".atom", '\u{e764}'); // 
-        m.insert(".bashprofile", '\u{e615}'); // 
-        m.insert(".bashrc", '\u{f489}'); // 
-        m.insert(".git", '\u{f1d3}'); // 
-        m.insert(".gitattributes", '\u{f1d3}'); // 
-        m.insert(".gitconfig", '\u{f1d3}'); // 
-        m.insert(".github", '\u{f408}'); // 
-        m.insert(".gitignore", '\u{f1d3}'); // 
-        m.insert(".gitignore_global", '\u{f1d3}'); // 
-        m.insert(".gitmodules", '\u{f1d3}'); // 
-        m.insert(".rvm", '\u{e21e}'); // 
-        m.insert(".vimrc", '\u{e7c5}'); // 
-        m.insert(".vscode", '\u{e70c}'); // 
-        m.insert(".zshrc", '\u{f489}'); // 
-        m.insert(".emacs", '\u{e632}'); // 
-        m.insert("LICENSE", '\u{f02d}'); // 
-        m.insert("Cargo.lock", '\u{e7a8}'); // 
-        m.insert("bin", '\u{e5fc}'); // 
-        m.insert("config", '\u{e5fc}'); // 
-        m.insert("docker-compose.yml", '\u{f308}'); // 
-        m.insert("Dockerfile", '\u{f308}'); // 
-        m.insert("Earthfile", '\u{f0ac}'); // 
-        m.insert("ds_store", '\u{f179}'); // 
-        m.insert("gitignore_global", '\u{f1d3}'); // 
-        m.insert("gitlab-ci.yml", '\u{f296}'); // 
-        m.insert("go.mod", '\u{e626}'); // 
-        m.insert("go.sum", '\u{e626}'); // 
-        m.insert("gradle", '\u{e256}'); // 
-        m.insert("gruntfile.coffee", '\u{e611}'); // 
-        m.insert("gruntfile.js", '\u{e611}'); // 
-        m.insert("gruntfile.ls", '\u{e611}'); // 
-        m.insert("gulpfile.coffee", '\u{e610}'); // 
-        m.insert("gulpfile.js", '\u{e610}'); // 
-        m.insert("gulpfile.ls", '\u{e610}'); // 
-        m.insert("hidden", '\u{f023}'); // 
-        m.insert("include", '\u{e5fc}'); // 
-        m.insert("lib", '\u{f121}'); // 
-        m.insert("localized", '\u{f179}'); // 
-        m.insert("Makefile", '\u{f489}'); // 
-        m.insert("node_modules", '\u{e718}'); // 
-        m.insert("npmignore", '\u{e71e}'); // 
-        m.insert("PKGBUILD", '\u{f303}'); // 
-        m.insert("rubydoc", '\u{e73b}'); // 
-        m.insert("yarn.lock", '\u{e718}'); // 
-        m.insert("Vagrantfile", '\u{2371}'); //⍱
-
-        m
-    };
-}
-
 pub fn icon_for_file(file: &File<'_>) -> char {
-    let extensions = Box::new(FileExtensions);
-
-    if let Some(icon) = MAP_BY_NAME.get(file.name.as_str()) { *icon }
-    else if file.points_to_directory() {
+    if let Some(icon) = FILENAME_ICONS.get(file.name.as_str()) {
+        *icon
+    } else if file.points_to_directory() {
         if file.is_empty_dir() {
-          return '\u{f115}'  // 
+            '\u{f115}' // 
+        } else {
+            '\u{f07b}' // 
         }
-        match file.name.as_str() {
-            "bin"           => '\u{e5fc}', // 
-            ".git"          => '\u{f1d3}', // 
-            ".idea"         => '\u{e7b5}', // 
-            _               => '\u{f07b}'  // 
-
-        }
-    }
-    else if let Some(icon) = extensions.icon_file(file) { icon }
-    else if let Some(ext) = file.ext.as_ref() {
-        match ext.as_str() {
-            "7z"            => '\u{f410}', // 
-            "a"             => '\u{f17c}', // 
-            "acf"           => '\u{f1b6}', // 
-            "ai"            => '\u{e7b4}', // 
-            "android"       => '\u{e70e}', // 
-            "apk"           => '\u{e70e}', // 
-            "apple"         => '\u{f179}', // 
-            "asm"           => '\u{e637}', // 
-            "avi"           => '\u{f03d}', // 
-            "avif"          => '\u{f1c5}', // 
-            "avro"          => '\u{e60b}', // 
-            "awk"           => '\u{f489}', // 
-            "bash"          => '\u{f489}', // 
-            "bash_history"  => '\u{f489}', // 
-            "bash_profile"  => '\u{f489}', // 
-            "bashrc"        => '\u{f489}', // 
-            "bat"           => '\u{ebc4}', //  
-            "bats"          => '\u{f489}', // 
-            "bib"           => '\u{e69b}', // 
-            "bin"           => '\u{eae8}', // 
-            "bmp"           => '\u{f1c5}', // 
-            "bst"           => '\u{e69b}', // 
-            "bz"            => '\u{f410}', // 
-            "bz2"           => '\u{f410}', // 
-            "c"             => '\u{e61e}', // 
-            "c++"           => '\u{e61d}', // 
-            "cab"           => '\u{e70f}', // 
-            "cc"            => '\u{e61d}', // 
-            "cert"          => '\u{eafa}', // 
-            "cfg"           => '\u{e615}', // 
-            "cjs"           => '\u{e74e}', // 
-            "class"         => '\u{e256}', // 
-            "clj"           => '\u{e768}', // 
-            "cljs"          => '\u{e76a}', // 
-            "cls"           => '\u{e69b}', // 
-            "cmd"           => '\u{e70f}', // 
-            "coffee"        => '\u{f0f4}', // 
-            "conf"          => '\u{e615}', // 
-            "config"        => '\u{e615}', // 
-            "cp"            => '\u{e61d}', // 
-            "cpio"          => '\u{f410}', // 
-            "cpp"           => '\u{e61d}', // 
-            "crt"           => '\u{eafa}', // 
-            "cs"            => '\u{f031b}', // 󰌛
-            "csh"           => '\u{f489}', // 
-            "cshtml"        => '\u{f1fa}', // 
-            "csproj"        => '\u{f031b}', // 󰌛
-            "css"           => '\u{e749}', // 
-            "csv"           => '\u{f1c3}', // 
-            "csx"           => '\u{f031b}', // 󰌛
-            "cts"           => '\u{e628}', // 
-            "cu"            => '\u{e64b}', // 
-            "cxx"           => '\u{e61d}', // 
-            "d"             => '\u{e7af}', // 
-            "dart"          => '\u{e798}', // 
-            "db"            => '\u{f1c0}', // 
-            "deb"           => '\u{e77d}', // 
-            "desktop"       => '\u{ebd1}', // 
-            "diff"          => '\u{f440}', // 
-            "djvu"          => '\u{f02d}', // 
-            "dll"           => '\u{e70f}', // 
-            "doc"           => '\u{f1c2}', // 
-            "docx"          => '\u{f1c2}', // 
-            "drawio"        => '\u{ebba}', // 
-            "ds_store"      => '\u{f179}', // 
-            "DS_store"      => '\u{f179}', // 
-            "dump"          => '\u{f1c0}', // 
-            "ebook"         => '\u{e28b}', // 
-            "ebuild"        => '\u{f30d}', // 
-            "editorconfig"  => '\u{e615}', // 
-            "ejs"           => '\u{e618}', // 
-            "el"            => '\u{e632}', // 
-            "elm"           => '\u{e62c}', // 
-            "eml"           => '\u{f003}', //  
-            "env"           => '\u{f462}', // 
-            "eot"           => '\u{f031}', // 
-            "epub"          => '\u{e28a}', // 
-            "erb"           => '\u{e73b}', // 
-            "erl"           => '\u{e7b1}', // 
-            "ex"            => '\u{e62d}', // 
-            "exe"           => '\u{f17a}', // 
-            "exs"           => '\u{e62d}', // 
-            "fish"          => '\u{f489}', // 
-            "flac"          => '\u{f001}', // 
-            "flv"           => '\u{f03d}', // 
-            "font"          => '\u{f031}', // 
-            "fs"            => '\u{e7a7}', // 
-            "fsi"           => '\u{e7a7}', // 
-            "fsx"           => '\u{e7a7}', // 
-            "gdoc"          => '\u{f1c2}', // 
-            "gem"           => '\u{e21e}', // 
-            "gemfile"       => '\u{e21e}', // 
-            "gemspec"       => '\u{e21e}', // 
-            "gform"         => '\u{f298}', // 
-            "gif"           => '\u{f1c5}', // 
-            "git"           => '\u{f1d3}', // 
-            "gitattributes" => '\u{f1d3}', // 
-            "gitignore"     => '\u{f1d3}', // 
-            "gitmodules"    => '\u{f1d3}', // 
-            "go"            => '\u{e626}', // 
-            "gpg"           => '\u{e60a}', // 
-            "gradle"        => '\u{e256}', // 
-            "groovy"        => '\u{e775}', // 
-            "gsheet"        => '\u{f1c3}', // 
-            "gslides"       => '\u{f1c4}', // 
-            "guardfile"     => '\u{e21e}', // 
-            "gz"            => '\u{f410}', // 
-            "h"             => '\u{f0fd}', // 
-            "hbs"           => '\u{e60f}', // 
-            "hpp"           => '\u{f0fd}', // 
-            "hs"            => '\u{e777}', // 
-            "htm"           => '\u{f13b}', // 
-            "html"          => '\u{f13b}', // 
-            "hxx"           => '\u{f0fd}', // 
-            "ical"          => '\u{eab0}', // 
-            "icalendar"     => '\u{eab0}', // 
-            "ico"           => '\u{f1c5}', // 
-            "ics"           => '\u{eab0}', // 
-            "ifb"           => '\u{eab0}', // 
-            "image"         => '\u{f1c5}', // 
-            "img"           => '\u{e271}', // 
-            "iml"           => '\u{e7b5}', // 
-            "ini"           => '\u{f17a}', // 
-            "ipynb"         => '\u{e678}', // 
-            "iso"           => '\u{e271}', // 
-            "j2c"           => '\u{f1c5}', // 
-            "j2k"           => '\u{f1c5}', // 
-            "jad"           => '\u{e256}', // 
-            "jar"           => '\u{e256}', // 
-            "java"          => '\u{e256}', // 
-            "jfi"           => '\u{f1c5}', // 
-            "jfif"          => '\u{f1c5}', // 
-            "jif"           => '\u{f1c5}', // 
-            "jl"            => '\u{e624}', // 
-            "jmd"           => '\u{f48a}', // 
-            "jp2"           => '\u{f1c5}', // 
-            "jpe"           => '\u{f1c5}', // 
-            "jpeg"          => '\u{f1c5}', // 
-            "jpg"           => '\u{f1c5}', // 
-            "jpx"           => '\u{f1c5}', // 
-            "js"            => '\u{e74e}', // 
-            "json"          => '\u{e60b}', // 
-            "jsx"           => '\u{e7ba}', // 
-            "jxl"           => '\u{f1c5}', // 
-            "kdb"           => '\u{f23e}', // 
-            "kdbx"          => '\u{f23e}', // 
-            "key"           => '\u{eb11}', // 
-            "ko"            => '\u{f17c}', // 
-            "ksh"           => '\u{f489}', // 
-            "latex"         => '\u{e69b}', // 
-            "less"          => '\u{e758}', // 
-            "lhs"           => '\u{e777}', // 
-            "license"       => '\u{f02d}', // 
-            "localized"     => '\u{f179}', // 
-            "lock"          => '\u{f023}', // 
-            "log"           => '\u{f18d}', // 
-            "lua"           => '\u{e620}', // 
-            "lz"            => '\u{f410}', // 
-            "lz4"           => '\u{f410}', // 
-            "lzh"           => '\u{f410}', // 
-            "lzma"          => '\u{f410}', // 
-            "lzo"           => '\u{f410}', // 
-            "m"             => '\u{e61e}', // 
-            "ml"            => '\u{e67a}', // 
-            "mli"           => '\u{e67a}', // 
-            "mll"           => '\u{e67a}', // 
-            "mly"           => '\u{e67a}', // 
-            "mm"            => '\u{e61d}', // 
-            "m4a"           => '\u{f001}', // 
-            "magnet"        => '\u{f076}', // 
-            "markdown"      => '\u{f48a}', // 
-            "md"            => '\u{f48a}', // 
-            "mjs"           => '\u{e74e}', // 
-            "mk"            => '\u{f489}', // 
-            "mkd"           => '\u{f48a}', // 
-            "mkv"           => '\u{f03d}', // 
-            "mobi"          => '\u{e28b}', // 
-            "mov"           => '\u{f03d}', // 
-            "mp2"           => '\u{f001}', // 
-            "mp3"           => '\u{f001}', // 
-            "mp4"           => '\u{f03d}', // 
-            "msi"           => '\u{e70f}', // 
-            "mts"           => '\u{e628}', // 
-            "mustache"      => '\u{e60f}', // 
-            "nix"           => '\u{f313}', // 
-            "node"          => '\u{f0399}', // 󰎙
-            "npmignore"     => '\u{e71e}', // 
-            "o"             => '\u{eae8}', // 
-            "odp"           => '\u{f1c4}', // 
-            "ods"           => '\u{f1c3}', // 
-            "odt"           => '\u{f1c2}', // 
-            "ogg"           => '\u{f001}', // 
-            "ogv"           => '\u{f03d}', // 
-            "org"           => '\u{e633}', // 
-            "otf"           => '\u{f031}', // 
-            "out"           => '\u{eb2c}', // 
-            "part"          => '\u{f43a}', // 
-            "patch"         => '\u{f440}', // 
-            "pdf"           => '\u{f1c1}', // 
-            "pem"           => '\u{eb11}', // 
-            "php"           => '\u{e73d}', // 
-            "pl"            => '\u{e769}', // 
-            "plx"           => '\u{e769}', // 
-            "pm"            => '\u{e769}', // 
-            "png"           => '\u{f1c5}', // 
-            "pod"           => '\u{e769}', // 
-            "ppt"           => '\u{f1c4}', // 
-            "pptx"          => '\u{f1c4}', // 
-            "procfile"      => '\u{e21e}', // 
-            "properties"    => '\u{e60b}', // 
-            "ps1"           => '\u{ebc7}', //  
-            "psd"           => '\u{e7b8}', // 
-            "psd1"          => '\u{ebc7}', //  
-            "psm1"          => '\u{ebc7}', //  
-            "pxm"           => '\u{f1c5}', // 
-            "py"            => '\u{e606}', // 
-            "pyc"           => '\u{e606}', // 
-            "qcow2"         => '\u{e271}', // 
-            "r"             => '\u{f25d}', // 
-            "rakefile"      => '\u{e21e}', // 
-            "rar"           => '\u{f410}', // 
-            "razor"         => '\u{f1fa}', // 
-            "rb"            => '\u{e21e}', // 
-            "rdata"         => '\u{f25d}', // 
-            "rdb"           => '\u{e76d}', // 
-            "rdoc"          => '\u{f48a}', // 
-            "rds"           => '\u{f25d}', // 
-            "readme"        => '\u{f48a}', // 
-            "rlib"          => '\u{e7a8}', // 
-            "rmd"           => '\u{f48a}', // 
-            "rmeta"         => '\u{e7a8}', // 
-            "rpm"           => '\u{e7bb}', // 
-            "rs"            => '\u{e7a8}', // 
-            "rspec"         => '\u{e21e}', // 
-            "rspec_parallel"=> '\u{e21e}', // 
-            "rspec_status"  => '\u{e21e}', // 
-            "rss"           => '\u{f09e}', // 
-            "rst"           => '\u{f15c}', // 
-            "rtf"           => '\u{f0219}', // 󰈙
-            "ru"            => '\u{e21e}', // 
-            "rubydoc"       => '\u{e73b}', // 
-            "s"             => '\u{e637}', // 
-            "sass"          => '\u{e603}', // 
-            "scala"         => '\u{e737}', // 
-            "scss"          => '\u{e749}', // 
-            "service"       => '\u{eba2}', // 
-            "sh"            => '\u{f489}', // 
-            "shell"         => '\u{f489}', // 
-            "slim"          => '\u{e73b}', // 
-            "sln"           => '\u{e70c}', // 
-            "so"            => '\u{f17c}', // 
-            "sql"           => '\u{f1c0}', // 
-            "sqlite3"       => '\u{e7c4}', // 
-            "sty"           => '\u{e69b}', // 
-            "styl"          => '\u{e600}', // 
-            "stylus"        => '\u{e600}', // 
-            "svg"           => '\u{f1c5}', // 
-            "swift"         => '\u{e755}', // 
-            "t"             => '\u{e769}', // 
-            "tar"           => '\u{f410}', // 
-            "taz"           => '\u{f410}', // 
-            "tbz"           => '\u{f410}', // 
-            "tbz2"          => '\u{f410}', // 
-            "tex"           => '\u{e69b}', // 
-            "tgz"           => '\u{f410}', // 
-            "tiff"          => '\u{f1c5}', // 
-            "tlz"           => '\u{f410}', // 
-            "toml"          => '\u{e615}', // 
-            "torrent"       => '\u{e275}', // 
-            "ts"            => '\u{e628}', // 
-            "tsv"           => '\u{f1c3}', // 
-            "tsx"           => '\u{e7ba}', // 
-            "ttf"           => '\u{f031}', // 
-            "twig"          => '\u{e61c}', // 
-            "txt"           => '\u{f15c}', // 
-            "txz"           => '\u{f410}', // 
-            "tz"            => '\u{f410}', // 
-            "tzo"           => '\u{f410}', // 
-            "unity"         => '\u{e721}', // 
-            "unity3d"       => '\u{e721}', // 
-            "vdi"           => '\u{e271}', // 
-            "vhd"           => '\u{e271}', // 
-            "video"         => '\u{f03d}', // 
-            "vim"           => '\u{e7c5}', // 
-            "vmdk"          => '\u{e271}', // 
-            "vue"           => '\u{f0844}', // 󰡄
-            "war"           => '\u{e256}', // 
-            "wav"           => '\u{f001}', // 
-            "webm"          => '\u{f03d}', // 
-            "webp"          => '\u{f1c5}', // 
-            "windows"       => '\u{f17a}', // 
-            "woff"          => '\u{f031}', // 
-            "woff2"         => '\u{f031}', // 
-            "xhtml"         => '\u{f13b}', // 
-            "xls"           => '\u{f1c3}', // 
-            "xlsm"          => '\u{f1c3}', // 
-            "xlsx"          => '\u{f1c3}', // 
-            "xml"           => '\u{f05c0}', // 󰗀
-            "xul"           => '\u{f05c0}', // 󰗀
-            "xz"            => '\u{f410}', // 
-            "yaml"          => '\u{f481}', // 
-            "yml"           => '\u{f481}', // 
-            "zig"           => '\u{21af}', // ↯
-            "zip"           => '\u{f410}', // 
-            "zsh"           => '\u{f489}', // 
-            "zsh-theme"     => '\u{f489}', // 
-            "zshrc"         => '\u{f489}', // 
-            "zst"           => '\u{f410}', // 
-            "svelte"        => '\u{e697}', // 
-            _               => '\u{f15b}'  // 
-        }
-    }
-    else {
-        '\u{f016}'
+    } else if let Some(ext) = file.ext.as_ref() {
+        *EXTENSION_ICONS.get(ext.as_str()).unwrap_or(&'\u{f15b}') // 
+    } else {
+        '\u{f016}' // 
     }
 }