BurntSushi / walkdir

Rust library for walking directories recursively.
The Unlicense
1.22k stars 107 forks source link

How would I emulate the following find command. #99

Closed cswl closed 6 years ago

cswl commented 6 years ago

I'm trying to find all git repos in a directory..

find . -name .git -type d -prune

It searches for directories with .git and then stops further recursing once it's been found..

So I tried something like this with filter_entry

fn is_git_repo(entry: &DirEntry) -> bool {
    let path = entry.path();
    // TODO: Handle errors
    let files = fs::read_dir(path).unwrap();
    files
        .map(|r| r.unwrap())
        .any(|s| s.file_name().to_str().unwrap() == ".git")
}

fn main() {
    let mut walker = WalkDir::new(p).into_iter();

    for entry in walker.filter_entry(|e| {
        // Only give directories
        e.file_type().is_dir()
    }) {
        let entry = entry?;

        if is_git_repo(&entry) {
            let path = entry.path();
            println!("{}", path.display());
        }
    }
}

It works.. but it's not efficient... I want to stop recursing after a git repo is found.. but I cant seem to code that logic in filter_entry

BurntSushi commented 6 years ago

Sorry, but I don't understand what you're asking. For example, it seems to me like your question has a trivial answer: break out of your loop once is_git_repo returns true. I suspect you're asking a different question though, but I don't know what it is. Please provide an example with the input, actual output and expected output.

If you want the is_git_repo predicate to influence filter_entry, then you need to use it in your filter_entry predicate.

Also, I have no idea why you are calling read_dir.

cswl commented 6 years ago

It gives all git repos in a folder..like:

/home/cswl/bin/bgrep
/home/cswl/bin/bindfs
/home/cswl/bin/bootimg-android/mkbootimg
/home/cswl/bin/mkbootfs
/home/cswl/bin/assert.sh
/home/cswl/bin/emojify
/home/cswl/bin/git-big-picture
/home/cswl/bin/LogCatch

I want filter_entry to give me that list...

    for entry in walker.filter_entry(|e| {
        e.file_type().is_dir() && is_git_repo(e)
    }) {
        let entry = entry?;
        path = entry.path();
        println!("{}", path.display());
    }

That is.. if it is a git repo.. dont recurse further... but the above logic with && doesnt work... since it would stop recursing at root.. since root isn't a git repo..

BurntSushi commented 6 years ago

I don't see how filter_entry can do what you want. Have you tried skip_current_dir?

cswl commented 6 years ago

This seems to work..

loop {
        let entry = match walker.next() {
            None => break,
            Some(e) => e?,
        };
        if entry.file_type().is_dir() { 
            if is_git_repo(&entry) { 
                // Stop further recursing if it is a git repo
                walker.skip_current_dir();
                let path = entry.path();
                println!("{}", path.display());
            }
        }
    }

However it's still could be further optimized if walker only yeilded directories...

BurntSushi commented 6 years ago

However it's still could be further optimized if walker only yeilded directories...

I disagree. The only way to not yield directories is to look at all of the entries. Which is what you're doing.

cswl commented 6 years ago

I guess so.. you're checking itone way or another..

Thanks for pointing me to .skip_current_dir It solved my main concern... so we can close this.