exa/src/options/filter.rs

201 lines
6.9 KiB
Rust
Raw Normal View History

use glob;
use fs::DotFilter;
use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns};
use options::{flags, Misfire};
use options::parser::MatchedFlags;
impl FileFilter {
/// Determines the set of file filter options to use, based on the users
/// command-line arguments.
pub fn deduce(matches: &MatchedFlags) -> Result<FileFilter, Misfire> {
Ok(FileFilter {
list_dirs_first: matches.has(&flags::DIRS_FIRST),
reverse: matches.has(&flags::REVERSE),
2017-03-26 16:35:50 +00:00
sort_field: SortField::deduce(matches)?,
dot_filter: DotFilter::deduce(matches)?,
2017-03-26 16:35:50 +00:00
ignore_patterns: IgnorePatterns::deduce(matches)?,
})
}
}
impl Default for SortField {
fn default() -> SortField {
SortField::Name(SortCase::Sensitive)
}
}
2017-07-26 19:45:01 +00:00
const SORTS: &[&str] = &[ "name", "Name", "size", "extension",
"Extension", "modified", "accessed",
"created", "inode", "type", "none" ];
impl SortField {
/// Determine the sort field to use, based on the presence of a “sort”
/// argument. This will return `Err` if the option is there, but does not
/// correspond to a valid field.
fn deduce(matches: &MatchedFlags) -> Result<SortField, Misfire> {
let word = match matches.get(&flags::SORT) {
Some(w) => w,
None => return Ok(SortField::default()),
};
if word == "name" || word == "filename" {
Ok(SortField::Name(SortCase::Sensitive))
}
else if word == "Name" || word == "Filename" {
Ok(SortField::Name(SortCase::Insensitive))
}
else if word == "size" || word == "filesize" {
Ok(SortField::Size)
}
else if word == "ext" || word == "extension" {
Ok(SortField::Extension(SortCase::Sensitive))
}
else if word == "Ext" || word == "Extension" {
Ok(SortField::Extension(SortCase::Insensitive))
}
else if word == "mod" || word == "modified" {
Ok(SortField::ModifiedDate)
}
else if word == "acc" || word == "accessed" {
Ok(SortField::AccessedDate)
}
else if word == "cr" || word == "created" {
Ok(SortField::CreatedDate)
}
else if word == "inode" {
Ok(SortField::FileInode)
}
else if word == "type" {
Ok(SortField::FileType)
}
else if word == "none" {
Ok(SortField::Unsorted)
}
else {
Err(Misfire::bad_argument(&flags::SORT, word, SORTS))
}
}
}
impl DotFilter {
pub fn deduce(matches: &MatchedFlags) -> Result<DotFilter, Misfire> {
match matches.count(&flags::ALL) {
0 => Ok(DotFilter::JustFiles),
1 => Ok(DotFilter::Dotfiles),
_ => if matches.has(&flags::TREE) { Err(Misfire::TreeAllAll) }
else { Ok(DotFilter::DotfilesAndDots) }
}
}
}
impl IgnorePatterns {
/// Determines the set of file filter options to use, based on the users
/// command-line arguments.
pub fn deduce(matches: &MatchedFlags) -> Result<IgnorePatterns, Misfire> {
let patterns = match matches.get(&flags::IGNORE_GLOB) {
None => Ok(Vec::new()),
Some(is) => is.to_string_lossy().split('|').map(|a| glob::Pattern::new(a)).collect(),
}?;
// TODO: is to_string_lossy really the best way to handle
// invalid UTF-8 there?
Ok(IgnorePatterns { patterns })
}
}
2017-07-26 19:45:01 +00:00
#[cfg(test)]
mod test {
use super::*;
use std::ffi::OsString;
use options::flags;
pub fn os(input: &'static str) -> OsString {
let mut os = OsString::new();
os.push(input);
os
}
macro_rules! test {
($name:ident: $type:ident <- $inputs:expr => $result:expr) => {
#[test]
fn $name() {
use options::parser::{Args, Arg};
2017-07-26 19:45:01 +00:00
use std::ffi::OsString;
2017-07-26 20:29:49 +00:00
static TEST_ARGS: &[&Arg] = &[ &flags::SORT, &flags::ALL, &flags::TREE, &flags::IGNORE_GLOB ];
2017-07-26 19:45:01 +00:00
let bits = $inputs.as_ref().into_iter().map(|&o| os(o)).collect::<Vec<OsString>>();
let results = Args(TEST_ARGS).parse(bits.iter());
assert_eq!($type::deduce(&results.unwrap().flags), $result);
2017-07-26 19:45:01 +00:00
}
};
}
mod sort_fields {
use super::*;
// Default behaviour
test!(empty: SortField <- [] => Ok(SortField::default()));
// Sort field arguments
test!(one_arg: SortField <- ["--sort=cr"] => Ok(SortField::CreatedDate));
test!(one_long: SortField <- ["--sort=size"] => Ok(SortField::Size));
test!(one_short: SortField <- ["-saccessed"] => Ok(SortField::AccessedDate));
test!(lowercase: SortField <- ["--sort", "name"] => Ok(SortField::Name(SortCase::Sensitive)));
test!(uppercase: SortField <- ["--sort", "Name"] => Ok(SortField::Name(SortCase::Insensitive)));
// Errors
test!(error: SortField <- ["--sort=colour"] => Err(Misfire::bad_argument(&flags::SORT, &os("colour"), super::SORTS)));
// Overriding
test!(overridden: SortField <- ["--sort=cr", "--sort", "mod"] => Ok(SortField::ModifiedDate));
test!(overridden_2: SortField <- ["--sort", "none", "--sort=Extension"] => Ok(SortField::Extension(SortCase::Insensitive)));
}
2017-07-26 20:01:22 +00:00
mod dot_filters {
use super::*;
// Default behaviour
test!(empty: DotFilter <- [] => Ok(DotFilter::JustFiles));
// --all
test!(all: DotFilter <- ["--all"] => Ok(DotFilter::Dotfiles));
test!(all_all: DotFilter <- ["--all", "-a"] => Ok(DotFilter::DotfilesAndDots));
test!(all_all_2: DotFilter <- ["-aa"] => Ok(DotFilter::DotfilesAndDots));
// --all and --tree
test!(tree_a: DotFilter <- ["-Ta"] => Ok(DotFilter::Dotfiles));
test!(tree_aa: DotFilter <- ["-Taa"] => Err(Misfire::TreeAllAll));
2017-07-26 20:01:22 +00:00
}
2017-07-26 20:29:49 +00:00
mod ignore_patternses {
use super::*;
use glob;
fn pat(string: &'static str) -> glob::Pattern {
glob::Pattern::new(string).unwrap()
}
// Various numbers of globs
test!(none: IgnorePatterns <- [] => Ok(IgnorePatterns { patterns: vec![] }));
test!(one: IgnorePatterns <- ["--ignore-glob", "*.ogg"] => Ok(IgnorePatterns { patterns: vec![ pat("*.ogg") ] }));
test!(two: IgnorePatterns <- ["--ignore-glob=*.ogg|*.MP3"] => Ok(IgnorePatterns { patterns: vec![ pat("*.ogg"), pat("*.MP3") ] }));
test!(loads: IgnorePatterns <- ["-I*|?|.|*"] => Ok(IgnorePatterns { patterns: vec![ pat("*"), pat("?"), pat("."), pat("*") ] }));
}
2017-07-26 19:45:01 +00:00
}