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