Cache the lowercased extension

Extensions aren't ever displayed in lowercase, just compared case-insensitively, so this makes sense.
This commit is contained in:
Ben S 2015-01-26 01:16:19 +00:00
parent 22a4dc90b9
commit da9d1f77d9
3 changed files with 61 additions and 22 deletions

View File

@ -1,5 +1,6 @@
use std::io::{fs, IoResult}; use std::io::{fs, IoResult};
use std::io; use std::io;
use std::ascii::AsciiExt;
use ansi_term::{ANSIString, Colour, Style}; use ansi_term::{ANSIString, Colour, Style};
use ansi_term::Style::Plain; use ansi_term::Style::Plain;
@ -370,16 +371,20 @@ impl<'a> File<'a> {
} }
} }
/// Extract an extension from a string, if one is present. /// Extract an extension from a string, if one is present, in lowercase.
/// ///
/// The extension is the series of characters after the last dot. This /// The extension is the series of characters after the last dot. This
/// deliberately counts dotfiles, so the ".git" folder has the extension "git". /// deliberately counts dotfiles, so the ".git" folder has the extension "git".
///
/// ASCII lowercasing is used because these extensions are only compared
/// against a pre-compiled list of extensions which are known to only exist
/// within ASCII, so it's alright.
fn ext<'a>(name: &'a str) -> Option<String> { fn ext<'a>(name: &'a str) -> Option<String> {
name.rfind('.').map(|p| name[p+1..].to_string()) name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase())
} }
#[cfg(test)] #[cfg(test)]
mod test { pub mod test {
pub use super::*; pub use super::*;
pub use column::{Cell, Column}; pub use column::{Cell, Column};
pub use std::io; pub use std::io;

View File

@ -2,13 +2,12 @@ use file::{File, GREY};
use self::FileType::*; use self::FileType::*;
use std::io; use std::io;
use std::ascii::AsciiExt;
use ansi_term::Style; use ansi_term::Style;
use ansi_term::Style::Plain; use ansi_term::Style::Plain;
use ansi_term::Colour::{Red, Green, Yellow, Blue, Cyan, Fixed}; use ansi_term::Colour::{Red, Green, Yellow, Blue, Cyan, Fixed};
#[derive(Copy)] #[derive(PartialEq, Debug, Copy)]
pub enum FileType { pub enum FileType {
Normal, Directory, Executable, Immediate, Compiled, Symlink, Special, Normal, Directory, Executable, Immediate, Compiled, Symlink, Special,
Image, Video, Music, Lossless, Compressed, Document, Temp, Crypto, Image, Video, Music, Lossless, Compressed, Document, Temp, Crypto,
@ -100,30 +99,29 @@ impl<'a> HasType for File<'a> {
else if name.starts_with("README") || BUILD_TYPES.iter().any(|&s| s == name) { else if name.starts_with("README") || BUILD_TYPES.iter().any(|&s| s == name) {
return Immediate; return Immediate;
} }
else if let Some(ref e) = self.ext { else if let Some(ref ext) = self.ext {
let ext = e.as_slice().to_ascii_lowercase(); if IMAGE_TYPES.iter().any(|&s| s == *ext) {
if IMAGE_TYPES.iter().any(|&s| s == ext) {
return Image; return Image;
} }
else if VIDEO_TYPES.iter().any(|&s| s == ext) { else if VIDEO_TYPES.iter().any(|&s| s == *ext) {
return Video; return Video;
} }
else if MUSIC_TYPES.iter().any(|&s| s == ext) { else if MUSIC_TYPES.iter().any(|&s| s == *ext) {
return Music; return Music;
} }
else if MUSIC_LOSSLESS.iter().any(|&s| s == ext) { else if MUSIC_LOSSLESS.iter().any(|&s| s == *ext) {
return Lossless; return Lossless;
} }
else if CRYPTO_TYPES.iter().any(|&s| s == ext) { else if CRYPTO_TYPES.iter().any(|&s| s == *ext) {
return Crypto; return Crypto;
} }
else if DOCUMENT_TYPES.iter().any(|&s| s == ext) { else if DOCUMENT_TYPES.iter().any(|&s| s == *ext) {
return Document; return Document;
} }
else if COMPRESSED_TYPES.iter().any(|&s| s == ext) { else if COMPRESSED_TYPES.iter().any(|&s| s == *ext) {
return Compressed; return Compressed;
} }
else if self.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == ext) { else if self.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == *ext) {
return Temp; return Temp;
} }
@ -135,7 +133,7 @@ impl<'a> HasType for File<'a> {
return Temp; return Temp;
} }
else { else {
if COMPILED_TYPES.iter().any(|&s| s == ext) { if COMPILED_TYPES.iter().any(|&s| s == *ext) {
return Compiled; return Compiled;
} }
else { else {
@ -147,3 +145,35 @@ impl<'a> HasType for File<'a> {
return Normal; // no filetype return Normal; // no filetype
} }
} }
#[cfg(test)]
mod test {
use super::*;
use file::File;
use file::test::dummy_stat;
#[test]
fn lowercase() {
let file = File::with_stat(dummy_stat(), &Path::new("/barracks.wav"), None);
assert_eq!(FileType::Lossless, file.get_type())
}
#[test]
fn uppercase() {
let file = File::with_stat(dummy_stat(), &Path::new("/BARRACKS.WAV"), None);
assert_eq!(FileType::Lossless, file.get_type())
}
#[test]
fn cargo() {
let file = File::with_stat(dummy_stat(), &Path::new("/Cargo.toml"), None);
assert_eq!(FileType::Immediate, file.get_type())
}
#[test]
fn not_cargo() {
let file = File::with_stat(dummy_stat(), &Path::new("/cargo.toml"), None);
assert_eq!(FileType::Normal, file.get_type())
}
}

View File

@ -8,8 +8,9 @@ use output::View;
use term::dimensions; use term::dimensions;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::slice::Iter; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use std::slice::Iter;
use self::Misfire::*; use self::Misfire::*;
@ -81,7 +82,7 @@ impl Options {
self.view.view(files) self.view.view(files)
} }
/// Transform the files somehow before listing them. /// Transform the files (sorting, reversing, filtering) before listing them.
pub fn transform_files<'a>(&self, mut files: Vec<File<'a>>) -> Vec<File<'a>> { pub fn transform_files<'a>(&self, mut files: Vec<File<'a>>) -> Vec<File<'a>> {
if !self.show_invisibles { if !self.show_invisibles {
@ -90,13 +91,16 @@ impl Options {
match self.sort_field { match self.sort_field {
SortField::Unsorted => {}, SortField::Unsorted => {},
SortField::Name => files.sort_by(|a, b| natord::compare(a.name.as_slice(), b.name.as_slice())), SortField::Name => files.sort_by(|a, b| natord::compare(&*a.name, &*b.name)),
SortField::Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)), SortField::Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)),
SortField::FileInode => files.sort_by(|a, b| a.stat.unstable.inode.cmp(&b.stat.unstable.inode)), SortField::FileInode => files.sort_by(|a, b| a.stat.unstable.inode.cmp(&b.stat.unstable.inode)),
SortField::Extension => files.sort_by(|a, b| { SortField::Extension => files.sort_by(|a, b| {
let exts = a.ext.clone().map(|e| e.to_ascii_lowercase()).cmp(&b.ext.clone().map(|e| e.to_ascii_lowercase())); if a.ext.cmp(&b.ext) == Ordering::Equal {
let names = a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase()); Ordering::Equal
exts.cmp(&names) }
else {
a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase())
}
}), }),
} }