BurntSushi / walkdir

Rust library for walking directories recursively.
The Unlicense
1.3k stars 109 forks source link

Extracting the relative path #204

Open konstin opened 1 week ago

konstin commented 1 week ago

I know this has been already discussed in #5, but it's been 8 years so i hope you don't mind me bringing this up again.

In many, if not most cases, where i'm using walkdir, i need the path relative to the root. For example, below is a naive recursive directory copy implementation, but this also applies to anything that needs to do includes or excludes with non-absolute matchers.

Currently, entry.path() does not seem to guarantee that the path starts with the walkdir root (or at least i didn't see that in the docs), the the unwrap could potentially fail, even if i know that i should be able to write code that never fails here.

From an API-perspective, it would be nice to have an infallible .relative_path(). Currently, there are usually two let relative = entry.path().strip_prefix(source_tree).expect("walkdir starts with root"); invocations when i call walkdir, one for filter_entry and one in the for loop body. This API would expose the infallible operation as such and could slim multi-line .filter_entry calls to a single line of .filter_entry(|entry| matches_directory(entry.relative_path()).

use std::error::Error;
use std::io::ErrorKind;
use std::path::Path;
use std::{fs, io};
use walkdir::WalkDir;

fn copy_dir_recursive(source: &Path, target: &Path) -> Result<(), Box<dyn Error>> {
    for entry in WalkDir::new(source) {
        let entry = entry?;
        let path = entry.path();

        let relative = path
            .strip_prefix(&source)
            .expect("walkdir starts with root");
        let out_path = target.join(relative);

        if entry.file_type().is_dir() {
            fs::create_dir_all(&out_path)?;
        } else if entry.file_type().is_file() {
            fs::copy(entry.path(), &out_path)?;
        } else {
            return Err(io::Error::new(
                ErrorKind::Other,
                format!(
                    "Unknown type {:?}: {}",
                    entry.file_type(),
                    entry.path().display()
                ),
            )
            .into());
        }
    }

    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    let source = Path::new("foo");
    let target = Path::new("/root/bar");

    copy_dir_recursive(&source, target)
}