Przeglądaj źródła

Use the natord library instead of our own sorter

I'm serious, the more functionality I can use external crates for, the better.
Ben S 11 lat temu
rodzic
commit
65f124fe39
6 zmienionych plików z 29 dodań i 124 usunięć
  1. 6 0
      Cargo.lock
  2. 4 1
      Cargo.toml
  3. 0 1
      src/exa.rs
  4. 0 4
      src/file.rs
  5. 19 18
      src/options.rs
  6. 0 100
      src/sort.rs

+ 6 - 0
Cargo.lock

@@ -3,6 +3,7 @@ name = "exa"
 version = "0.1.0"
 dependencies = [
  "ansi_term 0.3.0 (git+https://github.com/ogham/rust-ansi-term.git)",
+ "natord 1.0.0 (git+https://github.com/lifthrasiir/rust-natord.git)",
  "users 0.1.0 (git+https://github.com/ogham/rust-users)",
 ]
 
@@ -11,6 +12,11 @@ name = "ansi_term"
 version = "0.3.0"
 source = "git+https://github.com/ogham/rust-ansi-term.git#4b9ea6cf266053e1a771e75b935b4e54c586c139"
 
+[[package]]
+name = "natord"
+version = "1.0.0"
+source = "git+https://github.com/lifthrasiir/rust-natord.git#83ebf6e7999fe2646bca45d5f6800728a0bbd5c5"
+
 [[package]]
 name = "users"
 version = "0.1.0"

+ 4 - 1
Cargo.toml

@@ -10,4 +10,7 @@ name = "exa"
 git = "https://github.com/ogham/rust-ansi-term.git"
 
 [dependencies.users]
-git = "https://github.com/ogham/rust-users.git"
+git = "https://github.com/ogham/rust-users.git"
+
+[dependencies.natord]
+git = "https://github.com/lifthrasiir/rust-natord.git"

+ 0 - 1
src/exa.rs

@@ -28,7 +28,6 @@ pub mod format;
 pub mod file;
 pub mod filetype;
 pub mod options;
-pub mod sort;
 pub mod term;
 
 fn main() {

+ 0 - 4
src/file.rs

@@ -10,7 +10,6 @@ use users::{Users, OSUsers};
 use column::Column;
 use column::Column::*;
 use format::{format_metric_bytes, format_IEC_bytes};
-use sort::SortPart;
 use dir::Dir;
 use filetype::HasType;
 
@@ -29,7 +28,6 @@ pub struct File<'a> {
     pub ext:   Option<String>,
     pub path:  Path,
     pub stat:  io::FileStat,
-    pub parts: Vec<SortPart>,
 }
 
 impl<'a> File<'a> {
@@ -50,7 +48,6 @@ impl<'a> File<'a> {
             stat:  stat,
             name:  filename.clone(),
             ext:   File::ext(filename.clone()),
-            parts: SortPart::split_into_parts(filename.clone()),
         }
     }
 
@@ -210,7 +207,6 @@ impl<'a> File<'a> {
             stat:  stat,
             name:  filename.clone(),
             ext:   File::ext(filename.clone()),
-            parts: vec![],  // not needed
         });
 
         // Statting a path usually fails because the file at the

+ 19 - 18
src/options.rs

@@ -1,4 +1,5 @@
 extern crate getopts;
+extern crate natord;
 
 use file::File;
 use column::Column;
@@ -46,7 +47,7 @@ impl Options {
             getopts::optflag("1", "oneline",   "display one entry per line"),
             getopts::optflag("a", "all",       "show dot-files"),
             getopts::optflag("b", "binary",    "use binary prefixes in file sizes"),
-			getopts::optflag("d", "list-dirs", "list directories as regular files"),
+            getopts::optflag("d", "list-dirs", "list directories as regular files"),
             getopts::optflag("g", "group",     "show group as well as user"),
             getopts::optflag("h", "header",    "show a header row at the top"),
             getopts::optflag("H", "links",     "show number of hard links"),
@@ -60,27 +61,27 @@ impl Options {
         ];
 
         let matches = match getopts::getopts(args.tail(), &opts) {
-        	Ok(m) => m,
-        	Err(e) => {
-        		println!("Invalid options: {}", e);
-        		return Err(1);
-        	}
+            Ok(m) => m,
+            Err(e) => {
+                println!("Invalid options: {}", e);
+                return Err(1);
+            }
         };
 
         if matches.opt_present("help") {
-        	println!("exa - ls with more features\n\n{}", getopts::usage("Usage:\n  exa [options] [files...]", &opts))
-        	return Err(2);
+            println!("exa - ls with more features\n\n{}", getopts::usage("Usage:\n  exa [options] [files...]", &opts))
+            return Err(2);
         }
 
         Ok(Options {
-			header:          matches.opt_present("header"),
-			list_dirs:       matches.opt_present("list-dirs"),
-			path_strs:       if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
-			reverse:         matches.opt_present("reverse"),
-			show_invisibles: matches.opt_present("all"),
-			sort_field:      matches.opt_str("sort").map(|word| SortField::from_word(word)).unwrap_or(SortField::Name),
-			view:            Options::view(&matches),
-		})
+            header:          matches.opt_present("header"),
+            list_dirs:       matches.opt_present("list-dirs"),
+            path_strs:       if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() },
+            reverse:         matches.opt_present("reverse"),
+            show_invisibles: matches.opt_present("all"),
+            sort_field:      matches.opt_str("sort").map(|word| SortField::from_word(word)).unwrap_or(SortField::Name),
+            view:            Options::view(&matches),
+        })
     }
 
     fn view(matches: &getopts::Matches) -> View {
@@ -124,7 +125,7 @@ impl Options {
         }
 
         columns.push(FileName);
-		columns
+        columns
     }
 
     fn should_display(&self, f: &File) -> bool {
@@ -143,7 +144,7 @@ impl Options {
 
         match self.sort_field {
             SortField::Unsorted => {},
-            SortField::Name => files.sort_by(|a, b| a.parts.cmp(&b.parts)),
+            SortField::Name => files.sort_by(|a, b| natord::compare(a.name.as_slice(), b.name.as_slice())),
             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| {

+ 0 - 100
src/sort.rs

@@ -1,100 +0,0 @@
-use self::SortPart::*;
-use std::ascii::AsciiExt;
-
-// This is an implementation of "natural sort order". See
-// http://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
-// for more information and examples. It tries to sort "9" before
-// "10", which makes sense to those regular human types.
-
-// It works by splitting an input string into several parts, and then
-// comparing based on those parts. A SortPart derives TotalOrd, so a
-// Vec<SortPart> will automatically have natural sorting.
-
-#[deriving(Eq, Ord, PartialEq, PartialOrd)]
-pub enum SortPart {
-    Numeric(u64),
-    Stringular(String),
-}
-
-impl SortPart {
-    fn from_string(is_digit: bool, slice: &str) -> SortPart {
-        if is_digit {
-            // numbers too big for a u64 fall back into strings.
-            match from_str::<u64>(slice) {
-                Some(num) => Numeric(num),
-                None      => Stringular(slice.to_string()),
-            }
-        }
-        else {
-            SortPart::Stringular(slice.to_ascii_lower())
-        }
-    }
-
-    // The logic here is taken from my question at
-    // http://stackoverflow.com/q/23969191/3484614
-
-    pub fn split_into_parts(input: String) -> Vec<SortPart> {
-        let mut parts = vec![];
-
-        if input.is_empty() {
-            return parts
-        }
-
-        let mut is_digit = input.as_slice().char_at(0).is_digit(10);
-        let mut start = 0;
-
-        for (i, c) in input.as_slice().char_indices() {
-            if is_digit != c.is_digit(10) {
-                parts.push(SortPart::from_string(is_digit, input.as_slice().slice(start, i)));
-                is_digit = !is_digit;
-                start = i;
-            }
-        }
-
-        parts.push(SortPart::from_string(is_digit, input.as_slice().slice_from(start)));
-        parts
-    }
-}
-
-#[test]
-fn test_numeric() {
-    let bits = SortPart::split_into_parts("123456789".to_string());
-    assert!(bits == vec![ Numeric(123456789) ]);
-}
-
-
-#[test]
-fn test_stringular() {
-    let bits = SortPart::split_into_parts("toothpaste".to_string());
-    assert!(bits == vec![ Stringular("toothpaste".to_string()) ]);
-}
-
-#[test]
-fn test_empty() {
-    let bits = SortPart::split_into_parts("".to_string());
-    assert!(bits == vec![]);
-}
-
-#[test]
-fn test_one() {
-    let bits = SortPart::split_into_parts("123abc123".to_string());
-    assert!(bits == vec![ Numeric(123), Stringular("abc".to_string()), Numeric(123) ]);
-}
-
-#[test]
-fn test_two() {
-    let bits = SortPart::split_into_parts("final version 3.pdf".to_string());
-    assert!(bits == vec![ Stringular("final version ".to_string()), Numeric(3), Stringular(".pdf".to_string()) ]);
-}
-
-#[test]
-fn test_huge_number() {
-    let bits = SortPart::split_into_parts("9999999999999999999999999999999999999999999999999999999".to_string());
-    assert!(bits == vec![ Stringular("9999999999999999999999999999999999999999999999999999999".to_string()) ]);
-}
-
-#[test]
-fn test_case() {
-    let bits = SortPart::split_into_parts("123ABC123".to_string());
-    assert!(bits == vec![ Numeric(123), Stringular("abc".to_string()), Numeric(123) ]);
-}