diff --git a/src/app.rs b/src/app.rs index 4078991..efb44dd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -70,6 +70,8 @@ pub fn build_app() -> App<'static, 'static> { arg("file-type") .long("type") .short("t") + .multiple(true) + .number_of_values(1) .takes_value(true) .value_name("filetype") .possible_values(&["f", "file", "d", "directory", "l", "symlink"]) @@ -79,6 +81,8 @@ pub fn build_app() -> App<'static, 'static> { arg("extension") .long("extension") .short("e") + .multiple(true) + .number_of_values(1) .takes_value(true) .value_name("ext"), ) @@ -171,13 +175,14 @@ fn usage() -> HashMap<&'static str, Help> { on the search depth."); doc!(h, "file-type" , "Filter by type: f(ile), d(irectory), (sym)l(ink)" - , "Filter the search by type:\n \ + , "Filter the search by type (multiple allowable filetypes can be specified):\n \ 'f' or 'file': regular files\n \ 'd' or 'directory': directories\n \ 'l' or 'symlink': symbolic links"); doc!(h, "extension" , "Filter by file extension" - , "(Additionally) filter search results by their file extension."); + , "(Additionally) filter search results by their file extension. Multiple allowable file \ + extensions can be specified."); doc!(h, "exec" , "Execute a command for each search result" , "Execute a command for each search result.\n\ diff --git a/src/internal.rs b/src/internal.rs index 0f1e42d..e841fe7 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -6,6 +6,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. +use std::collections::HashSet; use std::process; use std::time; use std::io::Write; @@ -58,12 +59,12 @@ pub struct FdOptions { pub ls_colors: Option, /// The type of file to search for. All files other than the specified type will be ignored. - pub file_type: FileType, + pub file_types: HashSet, /// The extension to search for. Only entries matching the extension will be included. /// /// The value (if present) will be a lowercase string without leading dots. - pub extension: Option, + pub extensions: Option>, /// If a value is supplied, each item found will be used to generate and execute commands. pub command: Option, diff --git a/src/main.rs b/src/main.rs index 5b1d5db..bc1ecf6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -136,15 +136,20 @@ fn main() { .and_then(|n| u64::from_str_radix(n, 10).ok()) .map(time::Duration::from_millis), ls_colors, - file_type: match matches.value_of("file-type") { - Some("f") | Some("file") => FileType::RegularFile, - Some("d") | - Some("directory") => FileType::Directory, - Some("l") | Some("symlink") => FileType::SymLink, - _ => FileType::Any, + file_types: match matches.values_of("file-type") { + None => vec![FileType::RegularFile, + FileType::Directory, + FileType::SymLink] + .into_iter().collect(), + Some(values) => values.map(|value| match value { + "f" | "file" => FileType::RegularFile, + "d" | "directory" => FileType::Directory, + "l" | "symlink" => FileType::SymLink, + _ => FileType::RegularFile, + }).collect() }, - extension: matches.value_of("extension").map(|e| { - e.trim_left_matches('.').to_lowercase() + extensions: matches.values_of("extension").map(|exts| { + exts.map(|e| e.trim_left_matches('.').to_lowercase()).collect() }), command, exclude_patterns: matches diff --git a/src/walk.rs b/src/walk.rs index fa60894..1d5ee65 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -34,10 +34,9 @@ enum ReceiverMode { Streaming, } -/// The type of file to search for. -#[derive(Copy, Clone)] +/// The types of file to search for. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum FileType { - Any, RegularFile, Directory, SymLink, @@ -191,31 +190,21 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc, config: Arc) { } // Filter out unwanted file types. - match config.file_type { - FileType::Any => (), - FileType::RegularFile => { - if entry.file_type().map_or(true, |ft| !ft.is_file()) { - return ignore::WalkState::Continue; - } - } - FileType::Directory => { - if entry.file_type().map_or(true, |ft| !ft.is_dir()) { - return ignore::WalkState::Continue; - } - } - FileType::SymLink => { - if entry.file_type().map_or(true, |ft| !ft.is_symlink()) { - return ignore::WalkState::Continue; - } - } + if (entry.file_type().map_or(false, |ft| ft.is_file()) && + !config.file_types.contains(&FileType::RegularFile)) || + (entry.file_type().map_or(false, |ft| ft.is_dir()) && + !config.file_types.contains(&FileType::Directory)) || + (entry.file_type().map_or(false, |ft| ft.is_symlink()) && + !config.file_types.contains(&FileType::SymLink)) { + return ignore::WalkState::Continue; } // Filter out unwanted extensions. - if let Some(ref filter_ext) = config.extension { + if let Some(ref filter_exts) = config.extensions { let entry_ext = entry_path.extension().map( |e| e.to_string_lossy().to_lowercase(), ); - if entry_ext.map_or(true, |ext| ext != *filter_ext) { + if entry_ext.map_or(true, |ext| !filter_exts.contains(&ext)) { return ignore::WalkState::Continue; } } diff --git a/tests/tests.rs b/tests/tests.rs index 261f3a1..a550bd6 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -470,6 +470,11 @@ fn test_type() { one/two/three/d.foo", ); + te.assert_output( + &["--type", "f", "e1"], + "e1 e2", + ); + te.assert_output( &["--type", "d"], "one @@ -478,6 +483,15 @@ fn test_type() { one/two/three/directory_foo", ); + te.assert_output( + &["--type", "d", "--type", "l"], + "one + one/two + one/two/three + one/two/three/directory_foo + symlink", + ); + te.assert_output(&["--type", "l"], "symlink"); } @@ -502,6 +516,20 @@ fn test_extension() { one/two/three/d.foo", ); + te.assert_output( + &["--extension", ".foo", "--extension", "foo2"], + "a.foo + one/b.foo + one/two/c.foo + one/two/three/d.foo + one/two/C.Foo2", + ); + + te.assert_output( + &["--extension", ".foo", "a"], + "a.foo", + ); + te.assert_output(&["--extension", "foo2"], "one/two/C.Foo2"); }