Explorar o código

Cache the lowercased extension

Extensions aren't ever displayed in lowercase, just compared case-insensitively, so this makes sense.
Ben S %!s(int64=11) %!d(string=hai) anos
pai
achega
da9d1f77d9
Modificáronse 3 ficheiros con 61 adicións e 22 borrados
  1. 8 3
      src/file.rs
  2. 43 13
      src/filetype.rs
  3. 10 6
      src/options.rs

+ 8 - 3
src/file.rs

@@ -1,5 +1,6 @@
 use std::io::{fs, IoResult};
 use std::io;
+use std::ascii::AsciiExt;
 
 use ansi_term::{ANSIString, Colour, Style};
 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
 /// 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> {
-    name.rfind('.').map(|p| name[p+1..].to_string())
+    name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase())
 }
 
 #[cfg(test)]
-mod test {
+pub mod test {
     pub use super::*;
     pub use column::{Cell, Column};
     pub use std::io;

+ 43 - 13
src/filetype.rs

@@ -2,13 +2,12 @@ use file::{File, GREY};
 use self::FileType::*;
 
 use std::io;
-use std::ascii::AsciiExt;
 
 use ansi_term::Style;
 use ansi_term::Style::Plain;
 use ansi_term::Colour::{Red, Green, Yellow, Blue, Cyan, Fixed};
 
-#[derive(Copy)]
+#[derive(PartialEq, Debug, Copy)]
 pub enum FileType {
     Normal, Directory, Executable, Immediate, Compiled, Symlink, Special,
     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) {
             return Immediate;
         }
-        else if let Some(ref e) = self.ext {
-            let ext = e.as_slice().to_ascii_lowercase();
-            if IMAGE_TYPES.iter().any(|&s| s == ext) {
+        else if let Some(ref ext) = self.ext {
+            if IMAGE_TYPES.iter().any(|&s| s == *ext) {
                 return Image;
             }
-            else if VIDEO_TYPES.iter().any(|&s| s == ext) {
+            else if VIDEO_TYPES.iter().any(|&s| s == *ext) {
                 return Video;
             }
-            else if MUSIC_TYPES.iter().any(|&s| s == ext) {
+            else if MUSIC_TYPES.iter().any(|&s| s == *ext) {
                 return Music;
             }
-            else if MUSIC_LOSSLESS.iter().any(|&s| s == ext) {
+            else if MUSIC_LOSSLESS.iter().any(|&s| s == *ext) {
                 return Lossless;
             }
-            else if CRYPTO_TYPES.iter().any(|&s| s == ext) {
+            else if CRYPTO_TYPES.iter().any(|&s| s == *ext) {
                 return Crypto;
             }
-            else if DOCUMENT_TYPES.iter().any(|&s| s == ext) {
+            else if DOCUMENT_TYPES.iter().any(|&s| s == *ext) {
                 return Document;
             }
-            else if COMPRESSED_TYPES.iter().any(|&s| s == ext) {
+            else if COMPRESSED_TYPES.iter().any(|&s| s == *ext) {
                 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;
             }
 
@@ -135,7 +133,7 @@ impl<'a> HasType for File<'a> {
                 return Temp;
             }
             else {
-                if COMPILED_TYPES.iter().any(|&s| s == ext) {
+                if COMPILED_TYPES.iter().any(|&s| s == *ext) {
                     return Compiled;
                 }
                 else {
@@ -147,3 +145,35 @@ impl<'a> HasType for File<'a> {
         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())
+    }
+
+}

+ 10 - 6
src/options.rs

@@ -8,8 +8,9 @@ use output::View;
 use term::dimensions;
 
 use std::ascii::AsciiExt;
-use std::slice::Iter;
+use std::cmp::Ordering;
 use std::fmt;
+use std::slice::Iter;
 
 use self::Misfire::*;
 
@@ -81,7 +82,7 @@ impl Options {
         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>> {
 
         if !self.show_invisibles {
@@ -90,13 +91,16 @@ impl Options {
 
         match self.sort_field {
             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::FileInode => files.sort_by(|a, b| a.stat.unstable.inode.cmp(&b.stat.unstable.inode)),
             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()));
-                let names = a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase());
-                exts.cmp(&names)
+                if a.ext.cmp(&b.ext) == Ordering::Equal {
+                    Ordering::Equal
+                }
+                else {
+                    a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase())
+                }
             }),
         }