use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use glob::glob;
use miette::{IntoDiagnostic, Result};
use tracing::{debug, warn};
#[derive(Debug, Clone)]
pub struct GlobResolver {
patterns: Vec<String>,
}
impl GlobResolver {
pub fn new(patterns: Vec<String>) -> Self {
Self { patterns }
}
pub fn resolve(&self) -> Result<ResolvedPaths> {
let mut dirs = HashSet::new();
let mut files = HashSet::new();
for pattern in &self.patterns {
debug!(?pattern, "resolving glob pattern");
let entries = glob(pattern).into_diagnostic()?;
for entry in entries {
match entry {
Ok(path) => {
if path.is_dir() {
debug!(?path, "resolved to directory");
dirs.insert(path);
} else if path.is_file() {
debug!(?path, "resolved to file");
files.insert(path);
} else {
debug!(?path, "skipping non-file, non-directory");
}
}
Err(e) => {
warn!("glob error for pattern {}: {}", pattern, e);
}
}
}
}
Ok(ResolvedPaths {
dirs: dirs.into_iter().collect(),
files: files.into_iter().collect(),
})
}
}
#[derive(Debug, Clone)]
pub struct ResolvedPaths {
pub dirs: Vec<PathBuf>,
pub files: Vec<PathBuf>,
}
impl ResolvedPaths {
pub fn all_paths(&self) -> Vec<&Path> {
self.dirs
.iter()
.map(|p| p.as_path())
.chain(self.files.iter().map(|p| p.as_path()))
.collect()
}
pub fn differs_from(&self, other: &ResolvedPaths) -> bool {
let self_set: HashSet<_> = self.all_paths().into_iter().collect();
let other_set: HashSet<_> = other.all_paths().into_iter().collect();
self_set != other_set
}
}