Forbid certain argument combinations

This commit is contained in:
Benjamin Sago 2015-01-13 00:31:30 +01:00
parent 3d59a48efe
commit 728e7dd976
4 changed files with 134 additions and 66 deletions

View File

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

View File

@ -98,5 +98,17 @@ fn main() {
println!("{}", e); println!("{}", e);
set_exit_status(3); 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);
},
}; };
} }

View File

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

View File

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