mirror of
https://github.com/sharkdp/fd
synced 2024-11-05 16:58:21 +00:00
Added support for filtering by multiple filetypes and extensions (#205)
Closes #177 Closes #199
This commit is contained in:
parent
4d66c84109
commit
faf934da4b
5 changed files with 62 additions and 34 deletions
|
@ -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\
|
||||
|
|
|
@ -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<LsColors>,
|
||||
|
||||
/// 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<FileType>,
|
||||
|
||||
/// 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<String>,
|
||||
pub extensions: Option<HashSet<String>>,
|
||||
|
||||
/// If a value is supplied, each item found will be used to generate and execute commands.
|
||||
pub command: Option<CommandTemplate>,
|
||||
|
|
21
src/main.rs
21
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
|
||||
|
|
33
src/walk.rs
33
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<Regex>, config: Arc<FdOptions>) {
|
|||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue