提交 728e7dd9 编写于 作者: B Benjamin Sago

Forbid certain argument combinations

上级 3d59a48e
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,
......
......@@ -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);
},
};
}
......@@ -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"))
}
}
......@@ -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,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册