use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use yog_ids::Format;
pub(crate) fn under(dir: &Path, wanted: &dyn Fn(&Path) -> bool) -> Vec<PathBuf> {
fn walk(dir: &Path, wanted: &dyn Fn(&Path) -> bool, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for path in entries.flatten().map(|e| e.path()) {
if path.is_dir() {
walk(&path, wanted, out);
} else if wanted(&path) {
out.push(path);
}
}
}
let mut out = Vec::new();
walk(dir, wanted, &mut out);
out.sort();
out
}
pub fn with_ext(dir: &Path, ext: &str) -> Vec<PathBuf> {
under(dir, &|p| p.extension().is_some_and(|e| e == ext))
}
pub fn of(dir: &Path, format: Format) -> Vec<PathBuf> {
with_ext(dir, format.ext())
}
pub(crate) fn fingerprint(files: &[PathBuf]) -> u64 {
let mut h = DefaultHasher::new();
for f in files {
f.hash(&mut h);
if let Ok(m) = std::fs::metadata(f) {
m.len().hash(&mut h);
m.modified()
.ok()
.and_then(|t| t.duration_since(SystemTime::UNIX_EPOCH).ok())
.hash(&mut h);
}
}
h.finish()
}
pub fn read_source(path: impl AsRef<Path>) -> std::io::Result<String> {
let bytes = std::fs::read(path)?;
yog_data::source::text(&bytes)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}
pub fn write_source(path: &Path, text: &str) -> Result<(), String> {
let bytes = yog_data::source::binary(text).map_err(|e| format!("{}: {e}", path.display()))?;
std::fs::write(path, bytes).map_err(|e| format!("{}: {e}", path.display()))
}