use std::path::{Path, PathBuf};
use rustc_hash::FxHashSet;
use serde_json::Value;
pub(super) fn has_matching_manifest_json(
root: &Path,
discovered_files: &[PathBuf],
candidate_index: Option<&super::registry::ConfigCandidateIndex>,
matches_manifest: impl Fn(&Value) -> bool,
) -> bool {
manifest_json_candidates(root, discovered_files)
.into_iter()
.filter(|path| match candidate_index {
Some(index) => path
.parent()
.is_some_and(|dir| index.dir_contains(dir, std::ffi::OsStr::new("manifest.json"))),
None => true,
})
.any(|path| {
let Ok(source) = std::fs::read_to_string(path) else {
return false;
};
parse_manifest_json(&source).is_some_and(|manifest| matches_manifest(&manifest))
})
}
pub(super) fn parse_manifest_json(source: &str) -> Option<Value> {
serde_json::from_str(source).ok()
}
fn manifest_json_candidates(root: &Path, discovered_files: &[PathBuf]) -> Vec<PathBuf> {
let mut seen = FxHashSet::default();
let mut candidates = Vec::new();
push_manifest_candidate(root, &mut seen, &mut candidates);
for file in discovered_files {
let mut current = file.parent();
while let Some(dir) = current {
if !dir.starts_with(root) {
break;
}
push_manifest_candidate(dir, &mut seen, &mut candidates);
if dir == root {
break;
}
current = dir.parent();
}
}
candidates
}
fn push_manifest_candidate(
dir: &Path,
seen: &mut FxHashSet<PathBuf>,
candidates: &mut Vec<PathBuf>,
) {
let candidate = dir.join("manifest.json");
if seen.insert(candidate.clone()) {
candidates.push(candidate);
}
}