Преглед изворни кода

Forbid certain argument combinations

Benjamin Sago пре 11 година
родитељ
комит
728e7dd976
4 измењених фајлова са 133 додато и 65 уклоњено
  1. 2 0
      src/column.rs
  2. 12 0
      src/exa.rs
  3. 118 65
      src/options.rs
  4. 1 0
      src/output.rs

+ 2 - 0
src/column.rs

@@ -1,5 +1,6 @@
 use std::iter::repeat;
 
+#[derive(PartialEq, Show)]
 pub enum Column {
     Permissions,
     FileName,
@@ -13,6 +14,7 @@ pub enum Column {
 
 impl Copy for Column { }
 
+#[derive(PartialEq, Show)]
 pub enum SizeFormat {
     DecimalBytes,
     BinaryBytes,

+ 12 - 0
src/exa.rs

@@ -98,5 +98,17 @@ fn main() {
             println!("{}", e);
             set_exit_status(3);
         },
+        Err(Conflict(a, b)) => {
+            println!("Option --{} conflicts with option {}", a, b);
+            set_exit_status(3);
+        },
+        Err(Useless(a, false, b)) => {
+            println!("Option --{} is useless without option --{}", a, b);
+            set_exit_status(3);
+        },
+        Err(Useless(a, true, b)) => {
+            println!("Option --{} is useless given option --{}", a, b);
+            set_exit_status(3);
+        },
     };
 }

+ 118 - 65
src/options.rs

@@ -10,6 +10,7 @@ use term::dimensions;
 use std::ascii::AsciiExt;
 use std::slice::Iter;
 
+#[derive(PartialEq, Show)]
 pub enum SortField {
     Unsorted, Name, Extension, Size, FileInode
 }
@@ -33,6 +34,7 @@ fn no_sort_field(field: &str) -> Error {
     Error::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--sort {}", field)))
 }
 
+#[derive(PartialEq, Show)]
 pub struct Options {
     pub list_dirs: bool,
     pub path_strs: Vec<String>,
@@ -42,10 +44,12 @@ pub struct Options {
     pub view: View,
 }
 
-#[derive(Show)]
+#[derive(PartialEq, Show)]
 pub enum Error {
     InvalidOptions(getopts::Fail),
     Help(String),
+    Conflict(&'static str, &'static str),
+    Useless(&'static str, bool, &'static str),
 }
 
 impl Options {
@@ -88,7 +92,7 @@ impl Options {
             reverse:         matches.opt_present("reverse"),
             show_invisibles: matches.opt_present("all"),
             sort_field:      sort_field,
-            view:            Options::view(&matches),
+            view:            try!(view(&matches)),
         })
     }
 
@@ -96,90 +100,113 @@ impl Options {
         self.path_strs.iter()
     }
 
-    fn view(matches: &getopts::Matches) -> View {
-        if matches.opt_present("long") {
-            View::Details(Options::columns(matches), matches.opt_present("header"))
+    pub fn transform_files<'a>(&self, unordered_files: Vec<File<'a>>) -> Vec<File<'a>> {
+        let mut files: Vec<File<'a>> = unordered_files.into_iter()
+            .filter(|f| self.should_display(f))
+            .collect();
+
+        match self.sort_field {
+            SortField::Unsorted => {},
+            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| {
+                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)
+            }),
         }
-        else if matches.opt_present("oneline") {
-            View::Lines
+
+        if self.reverse {
+            files.reverse();
+        }
+
+        files
+    }
+
+    fn should_display(&self, f: &File) -> bool {
+        if self.show_invisibles {
+            true
         }
         else {
-            match dimensions() {
-                None => View::Lines,
-                Some((width, _)) => View::Grid(matches.opt_present("across"), width),
-            }
+            !f.name.as_slice().starts_with(".")
         }
     }
+}
 
-    fn columns(matches: &getopts::Matches) -> Vec<Column> {
-        let mut columns = vec![];
-
-        if matches.opt_present("inode") {
-            columns.push(Inode);
+fn view(matches: &getopts::Matches) -> Result<View, Error> {
+    if matches.opt_present("long") {
+        if matches.opt_present("across") {
+            Err(Error::Useless("across", true, "long"))
         }
-
-        columns.push(Permissions);
-
-        if matches.opt_present("links") {
-            columns.push(HardLinks);
+        else if matches.opt_present("oneline") {
+            Err(Error::Useless("across", true, "long"))
         }
-
-        if matches.opt_present("binary") {
-            columns.push(FileSize(SizeFormat::BinaryBytes))
+        else {
+            Ok(View::Details(try!(columns(matches)), matches.opt_present("header")))
         }
-        else if matches.opt_present("bytes") {
-            columns.push(FileSize(SizeFormat::JustBytes))
+    }
+    else if matches.opt_present("binary") {
+        Err(Error::Useless("binary", false, "long"))
+    }
+    else if matches.opt_present("bytes") {
+        Err(Error::Useless("bytes", false, "long"))
+    }
+    else if matches.opt_present("oneline") {
+        if matches.opt_present("across") {
+            Err(Error::Useless("across", true, "oneline"))
         }
         else {
-            columns.push(FileSize(SizeFormat::DecimalBytes))
+            Ok(View::Lines)
         }
-
-        if matches.opt_present("blocks") {
-            columns.push(Blocks);
+    }
+    else {
+        match dimensions() {
+            None => Ok(View::Lines),
+            Some((width, _)) => Ok(View::Grid(matches.opt_present("across"), width)),
         }
+    }
+}
 
-        columns.push(User);
+fn file_size(matches: &getopts::Matches) -> Result<SizeFormat, Error> {
+    let binary = matches.opt_present("binary");
+    let bytes = matches.opt_present("bytes");
 
-        if matches.opt_present("group") {
-            columns.push(Group);
-        }
+    match (binary, bytes) {
+        (true,  true ) => Err(Error::Conflict("binary", "bytes")),
+        (true,  false) => Ok(SizeFormat::BinaryBytes),
+        (false, true ) => Ok(SizeFormat::JustBytes),
+        (false, false) => Ok(SizeFormat::DecimalBytes),
+    }
+}
+
+fn columns(matches: &getopts::Matches) -> Result<Vec<Column>, Error> {
+    let mut columns = vec![];
 
-        columns.push(FileName);
-        columns
+    if matches.opt_present("inode") {
+        columns.push(Inode);
     }
 
-    fn should_display(&self, f: &File) -> bool {
-        if self.show_invisibles {
-            true
-        }
-        else {
-            !f.name.as_slice().starts_with(".")
-        }
+    columns.push(Permissions);
+
+    if matches.opt_present("links") {
+        columns.push(HardLinks);
     }
 
-    pub fn transform_files<'a>(&self, unordered_files: Vec<File<'a>>) -> Vec<File<'a>> {
-        let mut files: Vec<File<'a>> = unordered_files.into_iter()
-            .filter(|f| self.should_display(f))
-            .collect();
+    columns.push(FileSize(try!(file_size(matches))));
 
-        match self.sort_field {
-            SortField::Unsorted => {},
-            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| {
-                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 matches.opt_present("blocks") {
+        columns.push(Blocks);
+    }
 
-        if self.reverse {
-            files.reverse();
-        }
+    columns.push(User);
 
-        files
+    if matches.opt_present("group") {
+        columns.push(Group);
     }
+
+    columns.push(FileName);
+    Ok(columns)
 }
 
 #[cfg(test)]
@@ -220,8 +247,34 @@ mod test {
     }
 
     #[test]
-    fn view() {
-        let opts = Options::getopts(&[ "this file".to_string(), "that file".to_string() ]);
-        assert_eq!(opts.unwrap().path_strs, vec![ "this file".to_string(), "that file".to_string() ])
+    fn file_sizes() {
+        let opts = Options::getopts(&[ "--long".to_string(), "--binary".to_string(), "--bytes".to_string() ]);
+        assert_eq!(opts.unwrap_err(), Error::Conflict("binary", "bytes"))
+    }
+
+    #[test]
+    fn just_binary() {
+        let opts = Options::getopts(&[ "--binary".to_string() ]);
+        assert_eq!(opts.unwrap_err(), Error::Useless("binary", false, "long"))
     }
+
+    #[test]
+    fn just_bytes() {
+        let opts = Options::getopts(&[ "--bytes".to_string() ]);
+        assert_eq!(opts.unwrap_err(), Error::Useless("bytes", false, "long"))
+    }
+
+    #[test]
+    fn long_across() {
+        let opts = Options::getopts(&[ "--long".to_string(), "--across".to_string() ]);
+        assert_eq!(opts.unwrap_err(), Error::Useless("across", true, "long"))
+    }
+
+    #[test]
+    fn oneline_across() {
+        let opts = Options::getopts(&[ "--oneline".to_string(), "--across".to_string() ]);
+        assert_eq!(opts.unwrap_err(), Error::Useless("across", true, "oneline"))
+    }
+
+
 }

+ 1 - 0
src/output.rs

@@ -9,6 +9,7 @@ use users::OSUsers;
 use ansi_term::Style::Plain;
 use ansi_term::strip_formatting;
 
+#[derive(PartialEq, Show)]
 pub enum View {
     Details(Vec<Column>, bool),
     Lines,