use std::path::{Path, PathBuf};
use globset::{Glob, GlobSetBuilder};
use walkdir::WalkDir;
pub struct InputFile {
pub abs: PathBuf,
pub rel: PathBuf,
}
pub fn expand_path(raw: &str, base: &Path) -> Result<Vec<InputFile>, String> {
let raw_path = Path::new(raw);
if raw_path.is_absolute() {
if raw_path.is_dir() {
return expand_dir(raw_path, raw_path.parent().unwrap_or(raw_path));
}
if raw_path.exists() {
return Ok(vec![InputFile {
rel: raw_path
.file_name()
.map(PathBuf::from)
.unwrap_or_else(|| raw_path.to_path_buf()),
abs: raw_path.to_path_buf(),
}]);
}
return Err(format!("input not found: {}", raw_path.display()));
}
let candidate = base.join(raw_path);
if candidate.is_dir() {
return expand_dir(&candidate, base);
}
if candidate.is_file() {
return Ok(vec![InputFile {
rel: raw_path.to_path_buf(),
abs: candidate,
}]);
}
expand_glob(raw, base)
}
fn walk_files<'a>(root: &'a Path, base: &'a Path) -> impl Iterator<Item = InputFile> + use<'a> {
WalkDir::new(root)
.follow_links(false)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.map(move |entry| {
let abs = entry.path().to_path_buf();
let rel = abs
.strip_prefix(base)
.map(|p| p.to_path_buf())
.unwrap_or_else(|_| {
abs.file_name()
.map(PathBuf::from)
.unwrap_or_else(|| abs.clone())
});
InputFile { abs, rel }
})
}
fn expand_dir(dir: &Path, base: &Path) -> Result<Vec<InputFile>, String> {
let files: Vec<InputFile> = walk_files(dir, base).collect();
if files.is_empty() {
Err(format!("directory is empty: {}", dir.display()))
} else {
Ok(files)
}
}
fn expand_glob(pattern: &str, base: &Path) -> Result<Vec<InputFile>, String> {
let glob =
Glob::new(pattern).map_err(|e| format!("invalid glob pattern '{}': {}", pattern, e))?;
let mut builder = GlobSetBuilder::new();
builder.add(glob);
let set = builder
.build()
.map_err(|e| format!("glob build error: {}", e))?;
let files: Vec<InputFile> = walk_files(base, base)
.filter(|f| set.is_match(&f.rel))
.collect();
if files.is_empty() {
Err(format!("glob '{}' matched no files", pattern))
} else {
Ok(files)
}
}