1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-10-06 05:36:58 +02:00

ignore: add min_depth option

This mimics the eponymous option in `walkdir`.

Closes #3158, PR #3162
This commit is contained in:
Alvaro Parker
2025-09-28 19:15:18 -03:00
committed by Andrew Gallant
parent 9d8016d10c
commit 2924d0c4c0

View File

@@ -484,6 +484,7 @@ pub struct WalkBuilder {
paths: Vec<PathBuf>,
ig_builder: IgnoreBuilder,
max_depth: Option<usize>,
min_depth: Option<usize>,
max_filesize: Option<u64>,
follow_links: bool,
same_file_system: bool,
@@ -508,6 +509,7 @@ impl std::fmt::Debug for WalkBuilder {
.field("paths", &self.paths)
.field("ig_builder", &self.ig_builder)
.field("max_depth", &self.max_depth)
.field("min_depth", &self.min_depth)
.field("max_filesize", &self.max_filesize)
.field("follow_links", &self.follow_links)
.field("threads", &self.threads)
@@ -528,6 +530,7 @@ impl WalkBuilder {
paths: vec![path.as_ref().to_path_buf()],
ig_builder: IgnoreBuilder::new(),
max_depth: None,
min_depth: None,
max_filesize: None,
follow_links: false,
same_file_system: false,
@@ -542,6 +545,7 @@ impl WalkBuilder {
pub fn build(&self) -> Walk {
let follow_links = self.follow_links;
let max_depth = self.max_depth;
let min_depth = self.min_depth;
let sorter = self.sorter.clone();
let its = self
.paths
@@ -556,6 +560,9 @@ impl WalkBuilder {
if let Some(max_depth) = max_depth {
wd = wd.max_depth(max_depth);
}
if let Some(min_depth) = min_depth {
wd = wd.min_depth(min_depth);
}
if let Some(ref sorter) = sorter {
match sorter.clone() {
Sorter::ByName(cmp) => {
@@ -597,6 +604,7 @@ impl WalkBuilder {
paths: self.paths.clone().into_iter(),
ig_root: self.ig_builder.build(),
max_depth: self.max_depth,
min_depth: self.min_depth,
max_filesize: self.max_filesize,
follow_links: self.follow_links,
same_file_system: self.same_file_system,
@@ -621,6 +629,26 @@ impl WalkBuilder {
/// The default, `None`, imposes no depth restriction.
pub fn max_depth(&mut self, depth: Option<usize>) -> &mut WalkBuilder {
self.max_depth = depth;
if self.min_depth.is_some()
&& self.max_depth.is_some()
&& self.max_depth < self.min_depth
{
self.max_depth = self.min_depth;
}
self
}
/// The minimum depth to recurse.
///
/// The default, `None`, imposes no minimum depth restriction.
pub fn min_depth(&mut self, depth: Option<usize>) -> &mut WalkBuilder {
self.min_depth = depth;
if self.max_depth.is_some()
&& self.min_depth.is_some()
&& self.min_depth > self.max_depth
{
self.min_depth = self.max_depth;
}
self
}
@@ -1199,6 +1227,7 @@ pub struct WalkParallel {
ig_root: Ignore,
max_filesize: Option<u64>,
max_depth: Option<usize>,
min_depth: Option<usize>,
follow_links: bool,
same_file_system: bool,
threads: usize,
@@ -1298,6 +1327,7 @@ impl WalkParallel {
quit_now: quit_now.clone(),
active_workers: active_workers.clone(),
max_depth: self.max_depth,
min_depth: self.min_depth,
max_filesize: self.max_filesize,
follow_links: self.follow_links,
skip: self.skip.clone(),
@@ -1487,6 +1517,8 @@ struct Worker<'s> {
/// The maximum depth of directories to descend. A value of `0` means no
/// descension at all.
max_depth: Option<usize>,
/// The minimum depth of directories to descend.
min_depth: Option<usize>,
/// The maximum size a searched file can be (in bytes). If a file exceeds
/// this size it will be skipped.
max_filesize: Option<u64>,
@@ -1515,10 +1547,19 @@ impl<'s> Worker<'s> {
}
fn run_one(&mut self, mut work: Work) -> WalkState {
let should_visit = self
.min_depth
.map(|min_depth| work.dent.depth() >= min_depth)
.unwrap_or(true);
// If the work is not a directory, then we can just execute the
// caller's callback immediately and move on.
if work.is_symlink() || !work.is_dir() {
return self.visitor.visit(Ok(work.dent));
return if should_visit {
self.visitor.visit(Ok(work.dent))
} else {
WalkState::Continue
};
}
if let Some(err) = work.add_parents() {
let state = self.visitor.visit(Err(err));
@@ -1551,9 +1592,11 @@ impl<'s> Worker<'s> {
// entry before passing the error value.
let readdir = work.read_dir();
let depth = work.dent.depth();
let state = self.visitor.visit(Ok(work.dent));
if !state.is_continue() {
return state;
if should_visit {
let state = self.visitor.visit(Ok(work.dent));
if !state.is_continue() {
return state;
}
}
if !descend {
return WalkState::Skip;
@@ -2156,6 +2199,51 @@ mod tests {
);
}
#[test]
fn min_depth() {
let td = tmpdir();
mkdirp(td.path().join("a/b/c"));
wfile(td.path().join("foo"), "");
wfile(td.path().join("a/foo"), "");
wfile(td.path().join("a/b/foo"), "");
wfile(td.path().join("a/b/c/foo"), "");
let builder = WalkBuilder::new(td.path());
assert_paths(
td.path(),
&builder,
&["a", "a/b", "a/b/c", "foo", "a/foo", "a/b/foo", "a/b/c/foo"],
);
let mut builder = WalkBuilder::new(td.path());
assert_paths(
td.path(),
&builder.min_depth(Some(0)),
&["a", "a/b", "a/b/c", "foo", "a/foo", "a/b/foo", "a/b/c/foo"],
);
assert_paths(
td.path(),
&builder.min_depth(Some(1)),
&["a", "a/b", "a/b/c", "foo", "a/foo", "a/b/foo", "a/b/c/foo"],
);
assert_paths(
td.path(),
builder.min_depth(Some(2)),
&["a/b", "a/b/c", "a/b/c/foo", "a/b/foo", "a/foo"],
);
assert_paths(
td.path(),
builder.min_depth(Some(3)),
&["a/b/c", "a/b/c/foo", "a/b/foo"],
);
assert_paths(td.path(), builder.min_depth(Some(10)), &[]);
assert_paths(
td.path(),
builder.min_depth(Some(2)).max_depth(Some(1)),
&["a/b", "a/foo"],
);
}
#[test]
fn max_filesize() {
let td = tmpdir();