use crate::{
batch_io::BatchIO, error::GlobError, patterns::Patterns, predicates::Predicates, GlobOptions,
};
use camino::Utf8PathBuf;
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use walkdir::WalkDir;
fn check_for_cycles(path: &Path, visited: &mut HashSet<PathBuf>) -> bool {
if visited.contains(path) {
return true;
}
visited.insert(path.to_path_buf());
false
}
fn is_path_allowed(path: &Path, root_dir: &Option<PathBuf>) -> bool {
if let Some(root) = root_dir {
path.starts_with(root)
} else {
true
}
}
pub fn glob_sync(
patterns: Patterns,
opts: GlobOptions,
predicates: Option<Predicates>,
) -> Result<Vec<PathBuf>, GlobError> {
let mut results = Vec::new();
let root = opts.root_dir.clone().unwrap_or_else(|| PathBuf::from("."));
let mut visited_links = HashSet::new();
let batch_io = BatchIO::new(1000, opts.follow_symlinks);
for entry in WalkDir::new(&root)
.follow_links(opts.follow_symlinks)
.same_file_system(true)
.max_depth(opts.max_depth.unwrap_or(usize::MAX))
{
let dent = entry.map_err(GlobError::Walkdir)?;
let p = dent.path();
if !is_path_allowed(p, &opts.root_dir) {
continue;
}
if opts.follow_symlinks && check_for_cycles(p, &mut visited_links) {
return Err(GlobError::SymlinkCycle);
}
if p.is_dir() {
continue;
}
if let Ok(up) = Utf8PathBuf::from_path_buf(p.to_path_buf()) {
if !patterns.is_match(&up) {
continue;
}
if let Some(pred) = &predicates {
let meta = batch_io.stat(p)?;
if !pred.matches(&meta) {
continue;
}
}
results.push(p.to_path_buf());
}
}
Ok(results)
}