From d63c63be8cf8b6ecf75c1b5b8a2033ec7bb899f9 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Wed, 15 Apr 2020 16:17:01 +0200 Subject: [PATCH] Add --min-depth and --exact-depth Add new `--min-depth ` and `--exact-depth ` options in addition to the existing option to limit the maximum depth. closes #404 --- CHANGELOG.md | 2 ++ doc/fd.1 | 6 ++++++ src/app.rs | 26 +++++++++++++++++++++++++- src/main.rs | 7 ++++++- src/options.rs | 3 +++ src/walk.rs | 13 +++++++++++++ tests/tests.rs | 34 ++++++++++++++++++++++++++++++++++ 7 files changed, 89 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9722acf..56d2f18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ This can be useful to speed up searches in cases where you know that there are only N results. Using this option is also (slightly) faster than piping to `head -n ` where `fd` can only exit when it finds the search results ` + 1`. +- Add new `--min-depth ` and `--exact-depth ` options in addition to the existing option + to limit the maximum depth. See #404. - Add the alias `-1` for `--max-results=1`, see #561. (@SimplyDanny). - Support additional ANSI font styles in `LS_COLORS`: faint, slow blink, rapid blink, dimmed, hidden and strikethrough. diff --git a/doc/fd.1 b/doc/fd.1 index d1334e3..6575c2a 100644 --- a/doc/fd.1 +++ b/doc/fd.1 @@ -110,6 +110,12 @@ Limit directory traversal to at most .I d levels of depth. By default, there is no limit on the search depth. .TP +.BI "\-\-min\-depth " d +Only show search results starting at the given depth. See also: '--max-depth' and '--exact-depth'. +.TP +.BI "\-\-exact\-depth " d +Only show search results at the exact given depth. This is an alias for '--min-depth --max-depth '. +.TP .BI "\-t, \-\-type " filetype Filter search by type: .RS diff --git a/src/app.rs b/src/app.rs index 47a71dd..3b33d0e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -168,10 +168,11 @@ pub fn build_app() -> App<'static, 'static> { ), ) .arg( - Arg::with_name("depth") + Arg::with_name("max-depth") .long("max-depth") .short("d") .takes_value(true) + .value_name("depth") .help("Set maximum search depth (default: none)") .long_help( "Limit the directory traversal to a given depth. By default, there is no \ @@ -185,6 +186,29 @@ pub fn build_app() -> App<'static, 'static> { .hidden(true) .takes_value(true) ) + .arg( + Arg::with_name("min-depth") + .long("min-depth") + .takes_value(true) + .value_name("depth") + .hidden_short_help(true) + .long_help( + "Only show search results starting at the given depth. \ + See also: '--max-depth' and '--exact-depth'", + ), + ) + .arg( + Arg::with_name("exact-depth") + .long("exact-depth") + .takes_value(true) + .value_name("depth") + .hidden_short_help(true) + .conflicts_with_all(&["max-depth", "min-depth"]) + .long_help( + "Only show search results at the exact given depth. This is an alias for \ + '--min-depth --max-depth '.", + ), + ) .arg( Arg::with_name("file-type") .long("type") diff --git a/src/main.rs b/src/main.rs index 69653e1..1e4aefd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -226,8 +226,13 @@ fn run() -> Result { one_file_system: matches.is_present("one-file-system"), null_separator: matches.is_present("null_separator"), max_depth: matches - .value_of("depth") + .value_of("max-depth") .or_else(|| matches.value_of("rg-depth")) + .or_else(|| matches.value_of("exact-depth")) + .and_then(|n| usize::from_str_radix(n, 10).ok()), + min_depth: matches + .value_of("min-depth") + .or_else(|| matches.value_of("exact-depth")) .and_then(|n| usize::from_str_radix(n, 10).ok()), threads: std::cmp::max( matches diff --git a/src/options.rs b/src/options.rs index e7fb445..3f52516 100644 --- a/src/options.rs +++ b/src/options.rs @@ -40,6 +40,9 @@ pub struct Options { /// all files under subdirectories of the current directory, etc. pub max_depth: Option, + /// The minimum depth for reported entries, or `None`. + pub min_depth: Option, + /// The number of threads to use. pub threads: usize, diff --git a/src/walk.rs b/src/walk.rs index 2181a41..34b862c 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -283,6 +283,13 @@ impl DirEntry { DirEntry::BrokenSymlink(_) => None, } } + + pub fn depth(&self) -> Option { + match self { + DirEntry::Normal(e) => Some(e.depth()), + DirEntry::BrokenSymlink(_) => None, + } + } } fn spawn_senders( @@ -338,6 +345,12 @@ fn spawn_senders( } }; + if let Some(min_depth) = config.min_depth { + if entry.depth().map_or(true, |d| d < min_depth) { + return ignore::WalkState::Continue; + } + } + // Check the name first, since it doesn't require metadata let entry_path = entry.path(); diff --git a/tests/tests.rs b/tests/tests.rs index ce7df82..360d3db 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -679,6 +679,40 @@ fn test_max_depth() { ); } +/// Minimum depth (--min-depth) +#[test] +fn test_min_depth() { + let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES); + + te.assert_output( + &["--min-depth", "3"], + "one/two/c.foo + one/two/C.Foo2 + one/two/three + one/two/three/d.foo + one/two/three/directory_foo", + ); + + te.assert_output( + &["--min-depth", "4"], + "one/two/three/d.foo + one/two/three/directory_foo", + ); +} + +/// Exact depth (--exact-depth) +#[test] +fn test_exact_depth() { + let te = TestEnv::new(DEFAULT_DIRS, DEFAULT_FILES); + + te.assert_output( + &["--exact-depth", "3"], + "one/two/c.foo + one/two/C.Foo2 + one/two/three", + ); +} + /// Absolute paths (--absolute-path) #[test] fn test_absolute_path() {