From 78771a3a3ef66c910f452c8e20089fe25f5681e2 Mon Sep 17 00:00:00 2001 From: Ben S Date: Mon, 16 Jun 2014 23:34:22 +0100 Subject: [PATCH 1/5] Add a bunch more file types And an extra level of compiled files that don't get highlighted when their source files aren't present. I don't really like having two functions that do (almost) the same thing, but I have to due to the way colouring is done. --- file.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/file.rs b/file.rs index d5c09e5..797801b 100644 --- a/file.rs +++ b/file.rs @@ -7,15 +7,36 @@ use format::{format_metric_bytes, format_IEC_bytes}; use unix::{get_user_name, get_group_name}; use sort::SortPart; -static MEDIA_TYPES: &'static [&'static str] = &[ +static IMAGE_TYPES: &'static [&'static str] = &[ "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif", "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw", - "svg", "pdf", "stl", "eps", "dvi", "ps" ]; + "svg", "pdf", "stl", "eps", "dvi", "ps", "cbr", + "cbz", "xpm", "ico" ]; + +static VIDEO_TYPES: &'static [&'static str] = &[ + "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg", + "mpg", "ogm", "ogv", "vob", "wmv" ]; + +static MUSIC_TYPES: &'static [&'static str] = &[ + "aac", "m4a", "mp3", "ogg" ]; + +static MUSIC_LOSSLESS: &'static [&'static str] = &[ + "alac", "ape", "flac", "wav" ]; static COMPRESSED_TYPES: &'static [&'static str] = &[ "zip", "tar", "Z", "gz", "bz2", "a", "ar", "7z", "iso", "dmg", "tc", "rar", "par" ]; +static DOCUMENT_TYPES: &'static [&'static str] = &[ + "djvu", "doc", "docx", "eml", "eps", "odp", "ods", + "odt", "pdf", "ppt", "pptx", "xls", "xlsx" ]; + +static TEMP_TYPES: &'static [&'static str] = &[ + "tmp", "swp", "swo", "swn", "bak" ]; + +static CRYPTO_TYPES: &'static [&'static str] = &[ + "asc", "gpg", "sig", "signature", "pgp" ]; + // Instead of working with Rust's Paths, we have our own File object // that holds the Path and various cached information. Each file is // definitely going to have its filename used at least once, its stat @@ -74,6 +95,12 @@ impl<'a> File<'a> { format!("{}.{}", self.path.filestem_str().unwrap(), newext) } + // Highlight the compiled versions of files. Some of them, like .o, + // get special highlighting when they're alone because there's no + // point in existing without their source. Others can be perfectly + // content without their source files, such as how .js is valid + // without a .coffee. + fn get_source_files(&self) -> Vec { match self.ext { Some("class") => vec![self.with_extension("java")], // Java @@ -84,7 +111,23 @@ impl<'a> File<'a> { _ => vec![], } } + + fn get_source_files_usual(&self) -> Vec { + match self.ext { + Some("js") => vec![self.with_extension("coffee"), self.with_extension("ts")], // CoffeeScript, TypeScript + Some("css") => vec![self.with_extension("sass"), self.with_extension("less")], // SASS, Less + + Some("aux") => vec![self.with_extension("tex")], // TeX: auxiliary file + Some("bbl") => vec![self.with_extension("tex")], // BibTeX bibliography file + Some("blg") => vec![self.with_extension("tex")], // BibTeX log file + Some("lof") => vec![self.with_extension("tex")], // list of figures + Some("log") => vec![self.with_extension("tex")], // TeX log file + Some("lot") => vec![self.with_extension("tex")], // list of tables + Some("toc") => vec![self.with_extension("tex")], // table of contents + _ => vec![], + } + } pub fn display(&self, column: &Column) -> String { match *column { Permissions => self.permissions_string(), @@ -142,16 +185,40 @@ impl<'a> File<'a> { else if self.name.starts_with("README") { Yellow.bold().underline() } - else if self.ext.is_some() && MEDIA_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Purple.normal() + else if self.ext.is_some() && IMAGE_TYPES.iter().any(|&s| s == self.ext.unwrap()) { + Fixed(133).normal() + } + else if self.ext.is_some() && VIDEO_TYPES.iter().any(|&s| s == self.ext.unwrap()) { + Fixed(135).normal() + } + else if self.ext.is_some() && MUSIC_TYPES.iter().any(|&s| s == self.ext.unwrap()) { + Fixed(92).normal() + } + else if self.ext.is_some() && MUSIC_LOSSLESS.iter().any(|&s| s == self.ext.unwrap()) { + Fixed(93).normal() + } + else if self.ext.is_some() && CRYPTO_TYPES.iter().any(|&s| s == self.ext.unwrap()) { + Fixed(109).normal() + } + else if self.ext.is_some() && DOCUMENT_TYPES.iter().any(|&s| s == self.ext.unwrap()) { + Fixed(105).normal() } else if self.ext.is_some() && COMPRESSED_TYPES.iter().any(|&s| s == self.ext.unwrap()) { Red.normal() } + else if self.ext.is_some() && TEMP_TYPES.iter().any(|&s| s == self.ext.unwrap()) { + Fixed(244).normal() + } else { let source_files = self.get_source_files(); if source_files.len() == 0 { - Plain + let source_files_usual = self.get_source_files_usual(); + if source_files_usual.iter().any(|filename| Path::new(format!("{}/{}", self.path.dirname_str().unwrap(), filename)).exists()) { + Fixed(244).normal() + } + else { + Plain + } } else if source_files.iter().any(|filename| Path::new(format!("{}/{}", self.path.dirname_str().unwrap(), filename)).exists()) { Fixed(244).normal() From a5e404da5549fa3893569c3e0acbba4bca873b42 Mon Sep 17 00:00:00 2001 From: Ben S Date: Tue, 17 Jun 2014 00:27:05 +0100 Subject: [PATCH 2/5] Cache readdir results in a Dir object This new object is then passed down to the File, so it can see what files are around it. This means it doesn't need to re-query the filesystem several times per file, instead using the in-memory copy. Also, switch to using the built-in Path#with_extension method. --- dir.rs | 33 ++++++++++++++++++++++++++++++++ exa.rs | 13 +++++-------- file.rs | 58 ++++++++++++++++++++++++--------------------------------- 3 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 dir.rs diff --git a/dir.rs b/dir.rs new file mode 100644 index 0000000..5b4770b --- /dev/null +++ b/dir.rs @@ -0,0 +1,33 @@ +use std::io::fs; +use file::File; + +// The purpose of a Dir is to provide a cached list of the file paths +// in the directory being searched for. This object is then passed to +// the Files themselves, which can then check the status of their +// surrounding files, such as whether it needs to be coloured +// differently if a certain other file exists. + +pub struct Dir<'a> { + contents: Vec, +} + +impl<'a> Dir<'a> { + pub fn readdir(path: Path) -> Dir<'a> { + match fs::readdir(&path) { + Ok(paths) => Dir { + contents: paths, + }, + Err(e) => fail!("readdir: {}", e), + } + } + + pub fn files(&'a self) -> Vec> { + self.contents.iter().map(|path| File::from_path(path, self)).collect() + } + + pub fn contains(&self, path: &Path) -> bool { + self.contents.contains(path) + } +} + + diff --git a/exa.rs b/exa.rs index 8f9838f..d24260c 100644 --- a/exa.rs +++ b/exa.rs @@ -3,13 +3,14 @@ extern crate regex; #[phase(syntax)] extern crate regex_macros; use std::os; -use std::io::fs; use file::File; +use dir::Dir; use options::Options; pub mod colours; pub mod column; +pub mod dir; pub mod format; pub mod file; pub mod unix; @@ -40,13 +41,9 @@ fn main() { } fn exa(options: &Options, path: Path) { - let paths = match fs::readdir(&path) { - Ok(paths) => paths, - Err(e) => fail!("readdir: {}", e), - }; - - let unordered_files: Vec = paths.iter().map(|path| File::from_path(path)).collect(); - let files: Vec<&File> = options.transform_files(&unordered_files); + let dir = Dir::readdir(path); + let unsorted_files = dir.files(); + let files: Vec<&File> = options.transform_files(&unsorted_files); // The output gets formatted into columns, which looks nicer. To // do this, we have to write the results into a table, instead of diff --git a/file.rs b/file.rs index 797801b..9b01646 100644 --- a/file.rs +++ b/file.rs @@ -6,6 +6,7 @@ use column::{Column, Permissions, FileName, FileSize, User, Group}; use format::{format_metric_bytes, format_IEC_bytes}; use unix::{get_user_name, get_group_name}; use sort::SortPart; +use dir::Dir; static IMAGE_TYPES: &'static [&'static str] = &[ "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif", @@ -37,6 +38,7 @@ static TEMP_TYPES: &'static [&'static str] = &[ static CRYPTO_TYPES: &'static [&'static str] = &[ "asc", "gpg", "sig", "signature", "pgp" ]; + // Instead of working with Rust's Paths, we have our own File object // that holds the Path and various cached information. Each file is // definitely going to have its filename used at least once, its stat @@ -46,6 +48,7 @@ static CRYPTO_TYPES: &'static [&'static str] = &[ pub struct File<'a> { pub name: &'a str, + pub dir: &'a Dir<'a>, pub ext: Option<&'a str>, pub path: &'a Path, pub stat: io::FileStat, @@ -53,7 +56,7 @@ pub struct File<'a> { } impl<'a> File<'a> { - pub fn from_path(path: &'a Path) -> File<'a> { + pub fn from_path(path: &'a Path, parent: &'a Dir) -> File<'a> { // Getting the string from a filename fails whenever it's not // UTF-8 representable - just assume it is for now. let filename: &str = path.filename_str().unwrap(); @@ -68,6 +71,7 @@ impl<'a> File<'a> { return File { path: path, + dir: parent, stat: stat, name: filename, ext: File::ext(filename), @@ -90,40 +94,36 @@ impl<'a> File<'a> { fn is_tmpfile(&self) -> bool { self.name.ends_with("~") || (self.name.starts_with("#") && self.name.ends_with("#")) } - - fn with_extension(&self, newext: &'static str) -> String { - format!("{}.{}", self.path.filestem_str().unwrap(), newext) - } - + // Highlight the compiled versions of files. Some of them, like .o, // get special highlighting when they're alone because there's no // point in existing without their source. Others can be perfectly // content without their source files, such as how .js is valid // without a .coffee. - fn get_source_files(&self) -> Vec { + fn get_source_files(&self) -> Vec { match self.ext { - Some("class") => vec![self.with_extension("java")], // Java - Some("elc") => vec![self.name.chop()], // Emacs Lisp - Some("hi") => vec![self.with_extension("hs")], // Haskell - Some("o") => vec![self.with_extension("c"), self.with_extension("cpp")], // C, C++ - Some("pyc") => vec![self.name.chop()], // Python + Some("class") => vec![self.path.with_extension("java")], // Java + Some("elc") => vec![self.path.with_extension("el")], // Emacs Lisp + Some("hi") => vec![self.path.with_extension("hs")], // Haskell + Some("o") => vec![self.path.with_extension("c"), self.path.with_extension("cpp")], // C, C++ + Some("pyc") => vec![self.path.with_extension("py")], // Python _ => vec![], } } - fn get_source_files_usual(&self) -> Vec { + fn get_source_files_usual(&self) -> Vec { match self.ext { - Some("js") => vec![self.with_extension("coffee"), self.with_extension("ts")], // CoffeeScript, TypeScript - Some("css") => vec![self.with_extension("sass"), self.with_extension("less")], // SASS, Less + Some("js") => vec![self.path.with_extension("coffee"), self.path.with_extension("ts")], // CoffeeScript, TypeScript + Some("css") => vec![self.path.with_extension("sass"), self.path.with_extension("less")], // SASS, Less - Some("aux") => vec![self.with_extension("tex")], // TeX: auxiliary file - Some("bbl") => vec![self.with_extension("tex")], // BibTeX bibliography file - Some("blg") => vec![self.with_extension("tex")], // BibTeX log file - Some("lof") => vec![self.with_extension("tex")], // list of figures - Some("log") => vec![self.with_extension("tex")], // TeX log file - Some("lot") => vec![self.with_extension("tex")], // list of tables - Some("toc") => vec![self.with_extension("tex")], // table of contents + Some("aux") => vec![self.path.with_extension("tex")], // TeX: auxiliary file + Some("bbl") => vec![self.path.with_extension("tex")], // BibTeX bibliography file + Some("blg") => vec![self.path.with_extension("tex")], // BibTeX log file + Some("lof") => vec![self.path.with_extension("tex")], // list of figures + Some("log") => vec![self.path.with_extension("tex")], // TeX log file + Some("lot") => vec![self.path.with_extension("tex")], // list of tables + Some("toc") => vec![self.path.with_extension("tex")], // table of contents _ => vec![], } @@ -213,14 +213,14 @@ impl<'a> File<'a> { let source_files = self.get_source_files(); if source_files.len() == 0 { let source_files_usual = self.get_source_files_usual(); - if source_files_usual.iter().any(|filename| Path::new(format!("{}/{}", self.path.dirname_str().unwrap(), filename)).exists()) { + if source_files_usual.iter().any(|path| self.dir.contains(path)) { Fixed(244).normal() } else { Plain } } - else if source_files.iter().any(|filename| Path::new(format!("{}/{}", self.path.dirname_str().unwrap(), filename)).exists()) { + else if source_files.iter().any(|path| self.dir.contains(path)) { Fixed(244).normal() } else { @@ -256,13 +256,3 @@ impl<'a> File<'a> { } } } - -trait Chop { - fn chop(&self) -> String; -} - -impl<'a> Chop for &'a str { - fn chop(&self) -> String { - self.slice_to(self.len() - 1).to_string() - } -} \ No newline at end of file From 03ec414dfbb6211fe8b602fa5a2f1e85730f7b18 Mon Sep 17 00:00:00 2001 From: Ben S Date: Tue, 17 Jun 2014 09:35:40 +0100 Subject: [PATCH 3/5] Move file-type detection to its own module I'd much rather have this separate, as it has the definite potential to balloon up to a huge size and end up making the other parts of the file module hard to read. But on the other hand, it meant making a few more methods public on file... readability over cleanliness, I guess. --- exa.rs | 1 + file.rs | 95 +++--------------------------------------- filetype.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 89 deletions(-) create mode 100644 filetype.rs diff --git a/exa.rs b/exa.rs index d24260c..d25b0c7 100644 --- a/exa.rs +++ b/exa.rs @@ -13,6 +13,7 @@ pub mod column; pub mod dir; pub mod format; pub mod file; +pub mod filetype; pub mod unix; pub mod options; pub mod sort; diff --git a/file.rs b/file.rs index 9b01646..226bbf7 100644 --- a/file.rs +++ b/file.rs @@ -1,43 +1,13 @@ +use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan, Fixed}; use std::io::fs; use std::io; -use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan, Fixed}; use column::{Column, Permissions, FileName, FileSize, User, Group}; use format::{format_metric_bytes, format_IEC_bytes}; use unix::{get_user_name, get_group_name}; use sort::SortPart; use dir::Dir; - -static IMAGE_TYPES: &'static [&'static str] = &[ - "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif", - "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw", - "svg", "pdf", "stl", "eps", "dvi", "ps", "cbr", - "cbz", "xpm", "ico" ]; - -static VIDEO_TYPES: &'static [&'static str] = &[ - "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg", - "mpg", "ogm", "ogv", "vob", "wmv" ]; - -static MUSIC_TYPES: &'static [&'static str] = &[ - "aac", "m4a", "mp3", "ogg" ]; - -static MUSIC_LOSSLESS: &'static [&'static str] = &[ - "alac", "ape", "flac", "wav" ]; - -static COMPRESSED_TYPES: &'static [&'static str] = &[ - "zip", "tar", "Z", "gz", "bz2", "a", "ar", "7z", - "iso", "dmg", "tc", "rar", "par" ]; - -static DOCUMENT_TYPES: &'static [&'static str] = &[ - "djvu", "doc", "docx", "eml", "eps", "odp", "ods", - "odt", "pdf", "ppt", "pptx", "xls", "xlsx" ]; - -static TEMP_TYPES: &'static [&'static str] = &[ - "tmp", "swp", "swo", "swn", "bak" ]; - -static CRYPTO_TYPES: &'static [&'static str] = &[ - "asc", "gpg", "sig", "signature", "pgp" ]; - +use filetype::FileType; // Instead of working with Rust's Paths, we have our own File object // that holds the Path and various cached information. Each file is @@ -91,7 +61,7 @@ impl<'a> File<'a> { self.name.starts_with(".") } - fn is_tmpfile(&self) -> bool { + pub fn is_tmpfile(&self) -> bool { self.name.ends_with("~") || (self.name.starts_with("#") && self.name.ends_with("#")) } @@ -101,7 +71,7 @@ impl<'a> File<'a> { // content without their source files, such as how .js is valid // without a .coffee. - fn get_source_files(&self) -> Vec { + pub fn get_source_files(&self) -> Vec { match self.ext { Some("class") => vec![self.path.with_extension("java")], // Java Some("elc") => vec![self.path.with_extension("el")], // Emacs Lisp @@ -112,7 +82,7 @@ impl<'a> File<'a> { } } - fn get_source_files_usual(&self) -> Vec { + pub fn get_source_files_usual(&self) -> Vec { match self.ext { Some("js") => vec![self.path.with_extension("coffee"), self.path.with_extension("ts")], // CoffeeScript, TypeScript Some("css") => vec![self.path.with_extension("sass"), self.path.with_extension("less")], // SASS, Less @@ -173,60 +143,7 @@ impl<'a> File<'a> { } fn file_colour(&self) -> Style { - if self.stat.kind == io::TypeDirectory { - Blue.bold() - } - else if self.stat.perm.contains(io::UserExecute) { - Green.bold() - } - else if self.is_tmpfile() { - Fixed(244).normal() // midway between white and black - should show up as grey on all terminals - } - else if self.name.starts_with("README") { - Yellow.bold().underline() - } - else if self.ext.is_some() && IMAGE_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Fixed(133).normal() - } - else if self.ext.is_some() && VIDEO_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Fixed(135).normal() - } - else if self.ext.is_some() && MUSIC_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Fixed(92).normal() - } - else if self.ext.is_some() && MUSIC_LOSSLESS.iter().any(|&s| s == self.ext.unwrap()) { - Fixed(93).normal() - } - else if self.ext.is_some() && CRYPTO_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Fixed(109).normal() - } - else if self.ext.is_some() && DOCUMENT_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Fixed(105).normal() - } - else if self.ext.is_some() && COMPRESSED_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Red.normal() - } - else if self.ext.is_some() && TEMP_TYPES.iter().any(|&s| s == self.ext.unwrap()) { - Fixed(244).normal() - } - else { - let source_files = self.get_source_files(); - if source_files.len() == 0 { - let source_files_usual = self.get_source_files_usual(); - if source_files_usual.iter().any(|path| self.dir.contains(path)) { - Fixed(244).normal() - } - else { - Plain - } - } - else if source_files.iter().any(|path| self.dir.contains(path)) { - Fixed(244).normal() - } - else { - Fixed(137).normal() - } - } + FileType::from_file(self).style() } fn permissions_string(&self) -> String { diff --git a/filetype.rs b/filetype.rs new file mode 100644 index 0000000..ec4f85c --- /dev/null +++ b/filetype.rs @@ -0,0 +1,116 @@ +use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan, Fixed}; +use file::File; +use std::io; + +pub enum FileType { + Normal, Directory, Executable, Immediate, Compiled, + Image, Video, Music, Lossless, Compressed, Document, Temp, Crypto, +} + +static IMAGE_TYPES: &'static [&'static str] = &[ + "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif", + "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw", + "svg", "stl", "eps", "dvi", "ps", "cbr", + "cbz", "xpm", "ico" ]; + +static VIDEO_TYPES: &'static [&'static str] = &[ + "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg", + "mpg", "ogm", "ogv", "vob", "wmv" ]; + +static MUSIC_TYPES: &'static [&'static str] = &[ + "aac", "m4a", "mp3", "ogg" ]; + +static MUSIC_LOSSLESS: &'static [&'static str] = &[ + "alac", "ape", "flac", "wav" ]; + +static COMPRESSED_TYPES: &'static [&'static str] = &[ + "zip", "tar", "Z", "gz", "bz2", "a", "ar", "7z", + "iso", "dmg", "tc", "rar", "par" ]; + +static DOCUMENT_TYPES: &'static [&'static str] = &[ + "djvu", "doc", "docx", "eml", "eps", "odp", "ods", + "odt", "pdf", "ppt", "pptx", "xls", "xlsx" ]; + +static TEMP_TYPES: &'static [&'static str] = &[ + "tmp", "swp", "swo", "swn", "bak" ]; + +static CRYPTO_TYPES: &'static [&'static str] = &[ + "asc", "gpg", "sig", "signature", "pgp" ]; + +impl FileType { + pub fn style(&self) -> Style { + match *self { + Normal => Plain, + Directory => Blue.bold(), + Executable => Green.bold(), + Image => Fixed(133).normal(), + Video => Fixed(135).normal(), + Music => Fixed(92).normal(), + Lossless => Fixed(93).normal(), + Crypto => Fixed(109).normal(), + Document => Fixed(105).normal(), + Compressed => Red.normal(), + Temp => Fixed(244).normal(), + Immediate => Yellow.bold().underline(), + Compiled => Fixed(137).normal(), + } + } + + pub fn from_file(file: &File) -> FileType { + if file.stat.kind == io::TypeDirectory { + return Directory; + } + else if file.stat.perm.contains(io::UserExecute) { + return Executable; + } + else if file.ext.is_some() { + let ext = file.ext.unwrap(); + if IMAGE_TYPES.iter().any(|&s| s == ext) { + return Image; + } + else if VIDEO_TYPES.iter().any(|&s| s == ext) { + return Video; + } + else if MUSIC_TYPES.iter().any(|&s| s == ext) { + return Music; + } + else if MUSIC_LOSSLESS.iter().any(|&s| s == ext) { + return Lossless; + } + else if CRYPTO_TYPES.iter().any(|&s| s == ext) { + return Crypto; + } + else if DOCUMENT_TYPES.iter().any(|&s| s == ext) { + return Document; + } + else if COMPRESSED_TYPES.iter().any(|&s| s == ext) { + return Compressed; + } + else if file.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == ext) { + return Temp; + } + } + + if file.name.starts_with("README") { + return Immediate; + } + + let source_files = file.get_source_files(); + if source_files.len() == 0 { + let source_files_usual = file.get_source_files_usual(); + if source_files_usual.iter().any(|path| file.dir.contains(path)) { + Temp + } + else { + Normal + } + } + else if source_files.iter().any(|path| file.dir.contains(path)) { + Temp + } + else { + Compiled + } + } +} + From 75e8d829f311cd0e67d5dd80a52522c946d99c7a Mon Sep 17 00:00:00 2001 From: Ben S Date: Tue, 17 Jun 2014 22:17:22 +0100 Subject: [PATCH 4/5] Make file types a trait --- file.rs | 4 ++-- filetype.rs | 28 +++++++++++++++++----------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/file.rs b/file.rs index 226bbf7..a56953d 100644 --- a/file.rs +++ b/file.rs @@ -7,7 +7,7 @@ use format::{format_metric_bytes, format_IEC_bytes}; use unix::{get_user_name, get_group_name}; use sort::SortPart; use dir::Dir; -use filetype::FileType; +use filetype::HasType; // Instead of working with Rust's Paths, we have our own File object // that holds the Path and various cached information. Each file is @@ -143,7 +143,7 @@ impl<'a> File<'a> { } fn file_colour(&self) -> Style { - FileType::from_file(self).style() + self.get_type().style() } fn permissions_string(&self) -> String { diff --git a/filetype.rs b/filetype.rs index ec4f85c..fc8629c 100644 --- a/filetype.rs +++ b/filetype.rs @@ -55,16 +55,22 @@ impl FileType { Compiled => Fixed(137).normal(), } } +} - pub fn from_file(file: &File) -> FileType { - if file.stat.kind == io::TypeDirectory { +pub trait HasType { + fn get_type(&self) -> FileType; +} + +impl<'a> HasType for File<'a> { + fn get_type(&self) -> FileType { + if self.stat.kind == io::TypeDirectory { return Directory; } - else if file.stat.perm.contains(io::UserExecute) { + else if self.stat.perm.contains(io::UserExecute) { return Executable; } - else if file.ext.is_some() { - let ext = file.ext.unwrap(); + else if self.ext.is_some() { + let ext = self.ext.unwrap(); if IMAGE_TYPES.iter().any(|&s| s == ext) { return Image; } @@ -86,26 +92,26 @@ impl FileType { else if COMPRESSED_TYPES.iter().any(|&s| s == ext) { return Compressed; } - else if file.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == ext) { + else if self.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == ext) { return Temp; } } - if file.name.starts_with("README") { + if self.name.starts_with("README") { return Immediate; } - let source_files = file.get_source_files(); + let source_files = self.get_source_files(); if source_files.len() == 0 { - let source_files_usual = file.get_source_files_usual(); - if source_files_usual.iter().any(|path| file.dir.contains(path)) { + let source_files_usual = self.get_source_files_usual(); + if source_files_usual.iter().any(|path| self.dir.contains(path)) { Temp } else { Normal } } - else if source_files.iter().any(|path| file.dir.contains(path)) { + else if source_files.iter().any(|path| self.dir.contains(path)) { Temp } else { From f6b6d4d6a7fb2cf70b730fdaa34be84176e88fff Mon Sep 17 00:00:00 2001 From: Ben S Date: Wed, 18 Jun 2014 08:27:06 +0100 Subject: [PATCH 5/5] Merge the two source-file methods --- file.rs | 6 ------ filetype.rs | 40 +++++++++++++++++++++------------------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/file.rs b/file.rs index a56953d..6ed3139 100644 --- a/file.rs +++ b/file.rs @@ -78,12 +78,6 @@ impl<'a> File<'a> { Some("hi") => vec![self.path.with_extension("hs")], // Haskell Some("o") => vec![self.path.with_extension("c"), self.path.with_extension("cpp")], // C, C++ Some("pyc") => vec![self.path.with_extension("py")], // Python - _ => vec![], - } - } - - pub fn get_source_files_usual(&self) -> Vec { - match self.ext { Some("js") => vec![self.path.with_extension("coffee"), self.path.with_extension("ts")], // CoffeeScript, TypeScript Some("css") => vec![self.path.with_extension("sass"), self.path.with_extension("less")], // SASS, Less diff --git a/filetype.rs b/filetype.rs index fc8629c..c65f2be 100644 --- a/filetype.rs +++ b/filetype.rs @@ -15,7 +15,7 @@ static IMAGE_TYPES: &'static [&'static str] = &[ static VIDEO_TYPES: &'static [&'static str] = &[ "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg", - "mpg", "ogm", "ogv", "vob", "wmv" ]; + "mpg", "ogm", "ogv", "vob", "wmv" ]; static MUSIC_TYPES: &'static [&'static str] = &[ "aac", "m4a", "mp3", "ogg" ]; @@ -37,6 +37,9 @@ static TEMP_TYPES: &'static [&'static str] = &[ static CRYPTO_TYPES: &'static [&'static str] = &[ "asc", "gpg", "sig", "signature", "pgp" ]; +static COMPILED_TYPES: &'static [&'static str] = &[ + "class", "elc", "hi", "o", "pyc" ]; + impl FileType { pub fn style(&self) -> Style { match *self { @@ -69,6 +72,9 @@ impl<'a> HasType for File<'a> { else if self.stat.perm.contains(io::UserExecute) { return Executable; } + else if self.name.starts_with("README") { + return Immediate; + } else if self.ext.is_some() { let ext = self.ext.unwrap(); if IMAGE_TYPES.iter().any(|&s| s == ext) { @@ -95,28 +101,24 @@ impl<'a> HasType for File<'a> { else if self.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == ext) { return Temp; } - } - - if self.name.starts_with("README") { - return Immediate; - } - - let source_files = self.get_source_files(); - if source_files.len() == 0 { - let source_files_usual = self.get_source_files_usual(); - if source_files_usual.iter().any(|path| self.dir.contains(path)) { - Temp + + let source_files = self.get_source_files(); + if source_files.len() == 0 { + return Normal; + } + else if source_files.iter().any(|path| self.dir.contains(path)) { + return Temp; } else { - Normal + if COMPILED_TYPES.iter().any(|&s| s == ext) { + return Compiled; + } + else { + return Normal; + } } } - else if source_files.iter().any(|path| self.dir.contains(path)) { - Temp - } - else { - Compiled - } + return Normal; // no filetype } }