use std::path::{Path, PathBuf};
use filetime::FileTime;
use rayon::prelude::*;
use crate::filter::Filter;
use crate::meta::{FileTypeKind, meta_min};
use crate::{Error, Result, RunControl};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EntryKind {
Dir,
File,
Symlink(PathBuf),
}
#[derive(Debug, Clone)]
pub struct Entry {
pub rel: PathBuf,
pub kind: EntryKind,
pub len: u64,
pub mtime: FileTime,
pub mode: u32,
pub ino: u64,
pub dev: u64,
pub uid: u32,
pub gid: u32,
}
impl Entry {
#[must_use]
pub fn is_file(&self) -> bool {
matches!(self.kind, EntryKind::File)
}
#[must_use]
pub fn is_dir(&self) -> bool {
matches!(self.kind, EntryKind::Dir)
}
}
pub fn walk(root: &Path, threads: usize, filter: &Filter) -> Result<Vec<Entry>> {
walk_controlled(root, threads, filter, &RunControl::default())
}
pub fn walk_controlled(
root: &Path,
threads: usize,
filter: &Filter,
control: &RunControl,
) -> Result<Vec<Entry>> {
let parallelism = if threads == 0 {
jwalk::Parallelism::RayonDefaultPool {
busy_timeout: std::time::Duration::from_secs(1),
}
} else {
jwalk::Parallelism::RayonNewPool(threads)
};
let mut entries = Vec::new();
for dent in jwalk::WalkDir::new(root)
.parallelism(parallelism)
.skip_hidden(false)
.follow_links(false)
{
control.checkpoint()?;
let dent = dent.map_err(|e| Error::io(root, std::io::Error::other(e.to_string())))?;
let path = dent.path();
if path == root {
continue;
}
let rel = path
.strip_prefix(root)
.map_err(|_| Error::Containment(path.clone()))?
.to_path_buf();
if !filter.is_empty() && filter.is_excluded(&rel, dent.file_type().is_dir()) {
continue;
}
let m = meta_min(&path)?;
let kind = match m.kind {
FileTypeKind::Symlink => {
let target = std::fs::read_link(&path).map_err(|e| Error::io(&path, e))?;
EntryKind::Symlink(target)
}
FileTypeKind::Dir => EntryKind::Dir,
FileTypeKind::File => EntryKind::File,
FileTypeKind::Other => continue,
};
let len = if matches!(kind, EntryKind::File) {
m.len
} else {
0
};
entries.push(Entry {
rel,
kind,
len,
mtime: m.mtime,
mode: m.mode,
ino: m.ino,
dev: m.dev,
uid: m.uid,
gid: m.gid,
});
}
entries.par_sort_unstable_by(|a, b| a.rel.cmp(&b.rel));
Ok(entries)
}