use std::collections::{BTreeMap, BTreeSet};
use std::path::Path;
use blue_lang_check::{Code, RULES};
use blue_lang_runtime::pipeline::{check_entry, render, Checking};
use blue_lang_runtime::uses::Entry;
pub struct Census {
pub files: usize,
pub findings: BTreeMap<Code, BTreeSet<String>>,
}
impl Census {
#[must_use]
pub fn mismatches(&self) -> Vec<(Code, usize, u32)> {
RULES
.iter()
.filter_map(|r| {
let ratchet = r.ratchet?;
let measured = self.findings.get(&r.code).map_or(0, BTreeSet::len);
(measured != ratchet as usize).then_some((r.code, measured, ratchet))
})
.collect()
}
}
pub fn take(root: &Path) -> Result<Census, String> {
let files = blue_lang_pkg::corpus::files(root);
let loader = blue_lang_pkg::corpus::load_path(root, &files);
let mut findings: BTreeMap<Code, BTreeSet<String>> = BTreeMap::new();
for path in &files {
let text = std::fs::read_to_string(path).map_err(|e| format!("{}: {e}", path.display()))?;
let checked = check_entry(
Entry {
path: Some(path),
text: &text,
},
&loader,
None,
Checking::WithTests,
)
.map_err(|e| format!("{}: {e}", path.display()))?;
let ratcheted = |d: &&blue_lang_check::Diagnostic| d.code.rule().ratchet.is_some();
for d in checked
.outcome
.census
.iter()
.chain(checked.outcome.diagnostics.iter().filter(ratcheted))
{
findings
.entry(d.code)
.or_default()
.insert(render(&checked.program, d));
}
}
Ok(Census {
files: files.len(),
findings,
})
}