dirwalk 1.1.1

Platform-optimized recursive directory walker with metadata
Documentation
use crate::entry::Entry;
use crate::error::Error;
use crate::filter::Filter;
use crate::scan::scan_dir_prefixed;
use crate::walk::StorageHint;
use rayon::prelude::*;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Arc;

type DirResult = Result<(Vec<Entry>, Vec<Error>), Error>;

pub(crate) struct ParallelConfig {
    pub max_depth: u32,
    pub include_hidden: bool,
    pub follow_links: bool,
    pub filter: Filter,
    pub has_filter: bool,
    pub storage_hint: StorageHint,
}

pub(crate) fn parallel_walk(
    root: &Path,
    config: &ParallelConfig,
    threads: usize,
    pool: Option<Arc<rayon::ThreadPool>>,
) -> DirResult {
    let pool = match pool {
        Some(p) => p,
        None => Arc::new(
            rayon::ThreadPoolBuilder::new()
                .num_threads(threads)
                .build()
                .map_err(|e| Error::Io {
                    path: root.to_path_buf(),
                    source: io::Error::other(e),
                })?,
        ),
    };
    pool.install(|| walk_dir(root, "", 0, config))
}

fn walk_dir(abs: &Path, rel: &str, depth: u32, config: &ParallelConfig) -> DirResult {
    let raw = scan_dir_prefixed(abs, rel, config.storage_hint)?;
    let child_depth = depth + 1;

    let mut entries = Vec::with_capacity(raw.len());
    let mut subdirs: Vec<(PathBuf, String)> = Vec::with_capacity(raw.len() / 8); // ~1 in 8 entries are subdirs on typical trees

    for mut e in raw {
        if !config.include_hidden && e.is_hidden {
            continue;
        }

        e.depth = child_depth;

        let should_descend = e.is_dir || (e.is_symlink && config.follow_links);
        if should_descend && child_depth < config.max_depth {
            subdirs.push((abs.join(e.name()), e.relative_path.clone()));
        }

        if !config.has_filter || config.filter.matches(&e, abs) {
            entries.push(e);
        }
    }

    let sub_results: Vec<DirResult> = subdirs
        .into_par_iter()
        .map(|(child_abs, child_rel)| walk_dir(&child_abs, &child_rel, child_depth, config))
        .collect();

    let mut errors = Vec::new();
    for result in sub_results {
        match result {
            Ok((mut sub_entries, mut sub_errors)) => {
                entries.append(&mut sub_entries);
                errors.append(&mut sub_errors);
            }
            Err(e) => errors.push(e),
        }
    }

    Ok((entries, errors))
}