use std::collections::{BTreeMap, HashMap};
use std::fs;
use std::path::{Path, PathBuf};
use crate::freshness::record::sha256_hex;
use crate::repo;
use crate::walk::Ignored;
pub(super) type ConfigDigests = BTreeMap<String, String>;
pub(super) fn ancestors_up_to(root: &Path, dir: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
let mut cur = dir.to_path_buf();
while cur != root {
let Some(parent) = cur.parent() else {
break;
};
out.push(parent.to_path_buf());
if parent == root {
break;
}
cur = parent.to_path_buf();
}
out
}
pub(super) fn scan_ancestor_dirs(
root: &Path,
scope: &[String],
ignored: &Ignored,
csproj_files: &mut std::collections::HashSet<PathBuf>,
props_by_dir: &mut HashMap<PathBuf, String>,
digests: &mut ConfigDigests,
) {
for d in scope {
let scope_abs = if d == "." {
root.to_path_buf()
} else {
root.join(d)
};
for ancestor in ancestors_up_to(root, &scope_abs) {
scan_ancestor_dir(
root,
&ancestor,
ignored,
csproj_files,
props_by_dir,
digests,
);
}
}
}
pub(super) fn scan_ancestor_dir(
root: &Path,
dir: &Path,
ignored: &Ignored,
csproj_files: &mut std::collections::HashSet<PathBuf>,
props_by_dir: &mut HashMap<PathBuf, String>,
digests: &mut ConfigDigests,
) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let Ok(file_type) = entry.file_type() else {
continue;
};
if !file_type.is_file() {
continue;
}
let path = entry.path();
if !ignored.is_empty() && ignored.covers(&repo::rel_path(root, &path)) {
continue;
}
if path
.file_name()
.and_then(|n| n.to_str())
.is_some_and(|n| n.ends_with(".csproj"))
{
csproj_files.insert(path);
} else if is_directory_build_props(&path) {
if let Some(text) = read_lossy(root, &path, digests) {
props_by_dir.insert(dir.to_path_buf(), text);
}
}
}
}
pub(super) fn is_directory_build_props(path: &Path) -> bool {
path.file_name().and_then(|n| n.to_str()) == Some("Directory.Build.props")
}
pub(super) fn read_lossy(root: &Path, path: &Path, digests: &mut ConfigDigests) -> Option<String> {
let bytes = fs::read(path).ok()?;
digests.insert(repo::rel_path(root, path), sha256_hex(&bytes));
Some(String::from_utf8_lossy(&bytes).into_owned())
}