exa/src/info/filetype.rs

144 lines
5.1 KiB
Rust
Raw Normal View History

//! Tests for various types of file (video, image, compressed, etc).
//!
//! Currently this is dependent on the files name and extension, because
//! those are the only metadata that we have access to without reading the
//! files contents.
Extract trait above file name colours This commit meddles about with both the Colours and the FileExtensions. Even though all the renderable fields were turned into traits, the FileName struct kept on accessing fields directly on the Colours value instead of calling methods on it. It also did the usual amount of colour misappropriation (such as ‘punctuation’ instead of specifying ‘normal_arrow’) In preparation for when custom file colours are configurable (any day now), the colourise-file-by-kind functionality (links, sockets, or directories) was separated from the colourise-file-by-name functionality (images, videos, archives). The FileStyle struct already allowed for both to be separate; it was only changed so that a type other than FileExtensions could be used instead, as long as it implements the FileColours trait. (I feel like I should re-visit the naming of all these at some point in the future) The decision to separate the two means that FileExtensions is the one assigning the colours, rather than going through the fields on a Colours value, which have all been removed. This is why a bunch of arbitrary Styles now exist in filetype.rs. Because the decision on which colourise-file-by-name code to use (currently just the standard extensions, or nothing if we aren’t colourising) is now determined by the Colours type (instead of being derived), it’s possible to get it wrong. And wrong it was! There was a bug where file names were colourised even though the rest of the --long output wasn’t, and this wasn’t caught by the xtests. It is now.
2017-08-26 19:43:47 +00:00
use ansi_term::Style;
2018-12-07 23:43:31 +00:00
use crate::fs::File;
use crate::output::icons::FileIcon;
Massive theming and view options refactor This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code. The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly. This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary. The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction. There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct. Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
2020-10-22 21:34:00 +00:00
use crate::theme::FileColours;
2015-06-08 20:33:39 +00:00
Extract trait above file name colours This commit meddles about with both the Colours and the FileExtensions. Even though all the renderable fields were turned into traits, the FileName struct kept on accessing fields directly on the Colours value instead of calling methods on it. It also did the usual amount of colour misappropriation (such as ‘punctuation’ instead of specifying ‘normal_arrow’) In preparation for when custom file colours are configurable (any day now), the colourise-file-by-kind functionality (links, sockets, or directories) was separated from the colourise-file-by-name functionality (images, videos, archives). The FileStyle struct already allowed for both to be separate; it was only changed so that a type other than FileExtensions could be used instead, as long as it implements the FileColours trait. (I feel like I should re-visit the naming of all these at some point in the future) The decision to separate the two means that FileExtensions is the one assigning the colours, rather than going through the fields on a Colours value, which have all been removed. This is why a bunch of arbitrary Styles now exist in filetype.rs. Because the decision on which colourise-file-by-name code to use (currently just the standard extensions, or nothing if we aren’t colourising) is now determined by the Colours type (instead of being derived), it’s possible to get it wrong. And wrong it was! There was a bug where file names were colourised even though the rest of the --long output wasn’t, and this wasn’t caught by the xtests. It is now.
2017-08-26 19:43:47 +00:00
#[derive(Debug, Default, PartialEq)]
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. Its usually only present
/// in directories full of source code.
Cleanup clippy warnings warning: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> src/output/escape.rs:4:1 | 4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'a>>, good: Style, bad: Style) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | warning: this lifetime isn't used in the function definition --> src/output/escape.rs:4:15 | 4 | pub fn escape<'a>(string: String, bits: &mut Vec<ANSIString<'_>>, good: Style, bad: Style) { | ^^ | warning: single-character string constant used as pattern --> src/output/table.rs:310:41 | 310 | if file.starts_with(":") { | ^^^ help: try using a `char` instead: `':'` | warning: single-character string constant used as pattern --> src/output/table.rs:310:41 | 310 | if file.starts_with(":") { | ^^^ help: try using a `char` instead: `':'` | warning: methods called `new` usually return `Self` --> src/output/render/git.rs:38:5 | 38 | fn new(&self) -> Style; | ^^^^^^^^^^^^^^^^^^^^^^^ | warning: this lifetime isn't used in the function definition --> src/output/icons.rs:40:22 | 40 | pub fn iconify_style<'a>(style: Style) -> Style { | ^^ | warning: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint --> src/main.rs:11:10 | 11 | #![allow(clippy::find_map)] | ^^^^^^^^^^^^^^^^ | warning: redundant else block --> src/fs/dir.rs:124:18 | 124 | else { | __________________^ 125 | | return None 126 | | } | |_____________^ | warning: redundant else block --> src/options/view.rs:60:18 | 60 | else { | __________________^ 61 | | // the --tree case is handled by the DirAction parser later 62 | | return Ok(Self::Details(details)); 63 | | } | |_____________^ | warning: all variants have the same postfix: `Bytes` --> src/output/table.rs:170:1 | 170 | / pub enum SizeFormat { 171 | | 172 | | /// Format the file size using **decimal** prefixes, such as “kilo”, 173 | | /// “mega”, or “giga”. ... | 181 | | JustBytes, 182 | | } | |_^ | warning: all variants have the same postfix: `Bytes` --> src/output/table.rs:171:1 | 171 | / pub enum SizeFormat { 172 | | 173 | | /// Format the file size using **decimal** prefixes, such as “kilo”, 174 | | /// “mega”, or “giga”. ... | 182 | | JustBytes, 183 | | } | |_^ | warning: useless use of `format!` --> src/options/mod.rs:181:50 | 181 | return Err(OptionsError::Unsupported(format!( | __________________________________________________^ 182 | | "Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa" 183 | | ))); | |_____________^ help: consider using `.to_string()`: `"Options --git and --git-ignore can't be used because `git` feature was disabled in this build of exa".to_string()` | warning: stripping a prefix manually --> src/fs/filter.rs:287:33 | 287 | if n.starts_with('.') { &n[1..] } | ^^^^^^^ | warning: case-sensitive file extension comparison --> src/info/filetype.rs:24:19 | 24 | file.name.ends_with(".ninja") || | ^^^^^^^^^^^^^^^^^^^ |
2021-04-30 13:37:31 +00:00
#[allow(clippy::case_sensitive_file_extension_comparisons)]
2020-10-13 00:36:41 +00:00
fn is_immediate(&self, file: &File<'_>) -> bool {
2019-10-03 20:40:33 +00:00
file.name.to_lowercase().starts_with("readme") ||
file.name.ends_with(".ninja") ||
file.name_is_one_of( &[
2015-05-09 15:10:26 +00:00
"Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
"build.gradle", "pom.xml", "Rakefile", "package.json", "Gruntfile.js",
"Gruntfile.coffee", "BUILD", "BUILD.bazel", "WORKSPACE", "build.xml", "Podfile",
2020-10-15 20:38:38 +00:00
"webpack.config.js", "meson.build", "composer.json", "RoboFile.php", "PKGBUILD",
2020-10-18 08:41:44 +00:00
"Justfile", "Procfile", "Dockerfile", "Containerfile", "Vagrantfile", "Brewfile",
"Gemfile", "Pipfile", "build.sbt", "mix.exs", "bsconfig.json", "tsconfig.json",
2015-05-09 15:10:26 +00:00
])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
fn is_image(&self, file: &File<'_>) -> bool {
file.extension_is_one_of( &[
2021-07-02 10:02:06 +00:00
"png", "jfi", "jfif", "jif", "jpe", "jpeg", "jpg", "gif", "bmp",
"tiff", "tif", "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
"svg", "stl", "eps", "dvi", "ps", "cbr", "jpf", "cbz", "xpm",
"ico", "cr2", "orf", "nef", "heif",
2015-05-09 15:10:26 +00:00
])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
fn is_video(&self, file: &File<'_>) -> bool {
file.extension_is_one_of( &[
2018-10-02 05:47:51 +00:00
"avi", "flv", "m2v", "m4v", "mkv", "mov", "mp4", "mpeg",
2020-10-15 20:38:38 +00:00
"mpg", "ogm", "ogv", "vob", "wmv", "webm", "m2ts", "heic",
2015-05-09 15:10:26 +00:00
])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
fn is_music(&self, file: &File<'_>) -> bool {
file.extension_is_one_of( &[
"aac", "m4a", "mp3", "ogg", "wma", "mka", "opus",
2015-05-09 15:10:26 +00:00
])
}
2015-05-09 15:10:26 +00:00
// Lossless music, rather than any other kind of data...
2020-10-13 00:36:41 +00:00
fn is_lossless(&self, file: &File<'_>) -> bool {
file.extension_is_one_of( &[
2015-05-09 15:10:26 +00:00
"alac", "ape", "flac", "wav",
])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
fn is_crypto(&self, file: &File<'_>) -> bool {
file.extension_is_one_of( &[
"asc", "enc", "gpg", "pgp", "sig", "signature", "pfx", "p12",
2015-05-09 15:10:26 +00:00
])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
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",
2015-05-09 15:10:26 +00:00
])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
fn is_compressed(&self, file: &File<'_>) -> bool {
file.extension_is_one_of( &[
"zip", "tar", "Z", "z", "gz", "bz2", "a", "ar", "7z",
"iso", "dmg", "tc", "rar", "par", "tgz", "xz", "txz",
2019-10-03 20:40:33 +00:00
"lz", "tlz", "lzma", "deb", "rpm", "zst",
2015-05-09 15:10:26 +00:00
])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
fn is_temp(&self, file: &File<'_>) -> bool {
file.name.ends_with('~')
|| (file.name.starts_with('#') && file.name.ends_with('#'))
2021-08-23 01:11:35 +00:00
|| file.extension_is_one_of( &[ "tmp", "swp", "swo", "swn", "bak", "bkp", "bk" ])
}
2015-05-09 15:10:26 +00:00
2020-10-13 00:36:41 +00:00
fn is_compiled(&self, file: &File<'_>) -> bool {
2020-09-13 02:15:57 +00:00
if file.extension_is_one_of( &[ "class", "elc", "hi", "o", "pyc", "zwc", "ko" ]) {
2015-05-09 15:10:26 +00:00
true
}
else if let Some(dir) = file.parent_dir {
file.get_source_files().iter().any(|path| dir.contains(path))
}
2015-05-09 15:10:26 +00:00
else {
false
}
}
}
Extract trait above file name colours This commit meddles about with both the Colours and the FileExtensions. Even though all the renderable fields were turned into traits, the FileName struct kept on accessing fields directly on the Colours value instead of calling methods on it. It also did the usual amount of colour misappropriation (such as ‘punctuation’ instead of specifying ‘normal_arrow’) In preparation for when custom file colours are configurable (any day now), the colourise-file-by-kind functionality (links, sockets, or directories) was separated from the colourise-file-by-name functionality (images, videos, archives). The FileStyle struct already allowed for both to be separate; it was only changed so that a type other than FileExtensions could be used instead, as long as it implements the FileColours trait. (I feel like I should re-visit the naming of all these at some point in the future) The decision to separate the two means that FileExtensions is the one assigning the colours, rather than going through the fields on a Colours value, which have all been removed. This is why a bunch of arbitrary Styles now exist in filetype.rs. Because the decision on which colourise-file-by-name code to use (currently just the standard extensions, or nothing if we aren’t colourising) is now determined by the Colours type (instead of being derived), it’s possible to get it wrong. And wrong it was! There was a bug where file names were colourised even though the rest of the --long output wasn’t, and this wasn’t caught by the xtests. It is now.
2017-08-26 19:43:47 +00:00
impl FileColours for FileExtensions {
2020-10-13 00:36:41 +00:00
fn colour_file(&self, file: &File<'_>) -> Option<Style> {
Extract trait above file name colours This commit meddles about with both the Colours and the FileExtensions. Even though all the renderable fields were turned into traits, the FileName struct kept on accessing fields directly on the Colours value instead of calling methods on it. It also did the usual amount of colour misappropriation (such as ‘punctuation’ instead of specifying ‘normal_arrow’) In preparation for when custom file colours are configurable (any day now), the colourise-file-by-kind functionality (links, sockets, or directories) was separated from the colourise-file-by-name functionality (images, videos, archives). The FileStyle struct already allowed for both to be separate; it was only changed so that a type other than FileExtensions could be used instead, as long as it implements the FileColours trait. (I feel like I should re-visit the naming of all these at some point in the future) The decision to separate the two means that FileExtensions is the one assigning the colours, rather than going through the fields on a Colours value, which have all been removed. This is why a bunch of arbitrary Styles now exist in filetype.rs. Because the decision on which colourise-file-by-name code to use (currently just the standard extensions, or nothing if we aren’t colourising) is now determined by the Colours type (instead of being derived), it’s possible to get it wrong. And wrong it was! There was a bug where file names were colourised even though the rest of the --long output wasn’t, and this wasn’t caught by the xtests. It is now.
2017-08-26 19:43:47 +00:00
use ansi_term::Colour::*;
Some(match file {
f if self.is_temp(f) => Fixed(244).normal(),
Extract trait above file name colours This commit meddles about with both the Colours and the FileExtensions. Even though all the renderable fields were turned into traits, the FileName struct kept on accessing fields directly on the Colours value instead of calling methods on it. It also did the usual amount of colour misappropriation (such as ‘punctuation’ instead of specifying ‘normal_arrow’) In preparation for when custom file colours are configurable (any day now), the colourise-file-by-kind functionality (links, sockets, or directories) was separated from the colourise-file-by-name functionality (images, videos, archives). The FileStyle struct already allowed for both to be separate; it was only changed so that a type other than FileExtensions could be used instead, as long as it implements the FileColours trait. (I feel like I should re-visit the naming of all these at some point in the future) The decision to separate the two means that FileExtensions is the one assigning the colours, rather than going through the fields on a Colours value, which have all been removed. This is why a bunch of arbitrary Styles now exist in filetype.rs. Because the decision on which colourise-file-by-name code to use (currently just the standard extensions, or nothing if we aren’t colourising) is now determined by the Colours type (instead of being derived), it’s possible to get it wrong. And wrong it was! There was a bug where file names were colourised even though the rest of the --long output wasn’t, and this wasn’t caught by the xtests. It is now.
2017-08-26 19:43:47 +00:00
f if self.is_immediate(f) => Yellow.bold().underline(),
f if self.is_image(f) => Fixed(133).normal(),
f if self.is_video(f) => Fixed(135).normal(),
f if self.is_music(f) => Fixed(92).normal(),
f if self.is_lossless(f) => Fixed(93).normal(),
f if self.is_crypto(f) => Fixed(109).normal(),
f if self.is_document(f) => Fixed(105).normal(),
f if self.is_compressed(f) => Red.normal(),
f if self.is_compiled(f) => Fixed(137).normal(),
_ => return None,
})
}
}
impl FileIcon for FileExtensions {
2020-10-13 00:36:41 +00:00
fn icon_file(&self, file: &File<'_>) -> Option<char> {
2018-12-07 23:43:31 +00:00
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 {
None
}
}
}