use std::path::PathBuf;
use crate::tree::FileKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SymlinkPolicy {
#[default]
Skip,
Record,
Follow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SkipDirPreset {
Common,
Rust,
Node,
Python,
DotNet,
}
impl SkipDirPreset {
#[must_use]
pub const fn names(self) -> &'static [&'static str] {
match self {
Self::Common => &[".git"],
Self::Rust => &["target"],
Self::Node => &["node_modules", "dist"],
Self::Python => &[
"__pycache__",
".venv",
"venv",
".pytest_cache",
".mypy_cache",
".tox",
],
Self::DotNet => &["bin", "obj"],
}
}
#[must_use]
pub fn merge(presets: &[Self]) -> Vec<String> {
let mut names: Vec<String> = presets
.iter()
.flat_map(|p| p.names().iter().map(|n| (*n).to_owned()))
.collect();
names.sort_unstable();
names.dedup();
names
}
}
#[derive(Debug, Clone, Default)]
pub struct RecoveryRules {
pub exact_file_names: Vec<String>,
pub file_name_prefixes: Vec<String>,
pub directory_names: Vec<String>,
pub rel_path_suffixes: Vec<String>,
}
impl RecoveryRules {
pub(crate) fn matches(&self, rel_path: &str, name: &str, kind: FileKind) -> bool {
match kind {
FileKind::Directory => self.directory_names.iter().any(|d| d == name),
FileKind::File | FileKind::Symlink => {
self.exact_file_names.iter().any(|f| f == name)
|| self.file_name_prefixes.iter().any(|p| name.starts_with(p))
|| self
.rel_path_suffixes
.iter()
.any(|sfx| rel_path.ends_with(sfx))
}
}
}
}
#[derive(Debug, Clone)]
pub struct WalkOptions {
pub respect_gitignore: bool,
pub include_hidden: bool,
pub symlink_policy: SymlinkPolicy,
pub skip_dir_names: Vec<String>,
pub skip_path_prefixes: Vec<String>,
pub max_depth: Option<u32>,
pub recovery: Option<RecoveryRules>,
}
impl Default for WalkOptions {
fn default() -> Self {
Self {
respect_gitignore: true,
include_hidden: true,
symlink_policy: SymlinkPolicy::Skip,
skip_dir_names: SkipDirPreset::merge(&[
SkipDirPreset::Common,
SkipDirPreset::Rust,
SkipDirPreset::Node,
]),
skip_path_prefixes: Vec::new(),
max_depth: None,
recovery: None,
}
}
}
#[derive(Debug)]
pub enum WalkError {
RootNotFound,
RootNotADirectory,
Io {
path: PathBuf,
source: std::io::Error,
},
}
impl core::fmt::Display for WalkError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::RootNotFound => write!(f, "walk root not found"),
Self::RootNotADirectory => write!(f, "walk root is not a directory"),
Self::Io { path, source } => write!(f, "io error on {}: {source}", path.display()),
}
}
}
impl core::error::Error for WalkError {}