use crate::check::context::Ctx;
use crate::corpus::graph::query;
use crate::model::Doc;
use crate::workspace::owners::matcher;
const MANUAL: &str = "manual";
pub fn verify_commands(d: &Doc) -> Vec<String> {
d.list_field("verify").into_iter().filter(|c| !c.trim().eq_ignore_ascii_case(MANUAL)).collect()
}
fn is_gitlink(ctx: &Ctx, path: &str) -> bool {
ctx.corpus.cfg.spec_home.join(path).join(".git").exists()
}
pub fn selected(ctx: &Ctx) -> Vec<usize> {
let all = || (0..ctx.corpus.docs.len()).collect();
if ctx.scope.is_all() {
return all();
}
let full: Vec<_> = ctx.corpus.cfg.raw.verify.full_run_on.iter().filter_map(|p| matcher(p)).collect();
let hit = ctx.scope.paths.iter().find(|p| full.iter().any(|m| m.is_match(p)) && !neutral_config_edit(ctx, p));
if let Some(p) = hit {
say_full_run(ctx, p);
return all();
}
let mut out: Vec<usize> =
ctx.scope.paths.iter().flat_map(|p| query::governing(ctx.corpus, p).into_iter().map(|g| g.idx)).collect();
for (i, d) in ctx.corpus.docs.iter().enumerate() {
let commands = verify_commands(d);
let governs = d.rel().get("governs").cloned().unwrap_or_default();
let reached = ctx.scope.paths.iter().any(|p| {
commands.iter().any(|c| c.contains(p.as_str()))
|| (is_gitlink(ctx, p) && governs.iter().any(|g| crate::paths::is_below(g, p)))
});
if reached {
out.push(i);
}
}
out.extend(ctx.scope.touched.iter().copied());
out.sort();
out.dedup();
out
}
const NEUTRAL: &[&[&str]] = &[&["remotes"], &["prefix"], &["check", "baseline"]];
fn neutral_config_edit(ctx: &Ctx, path: &str) -> bool {
if path != "spec.toml" {
return false;
}
let parse = |t: Option<String>| t.and_then(|t| t.parse::<toml::Table>().ok()).map(strip);
match (parse(ctx.previous(path)), parse(ctx.current(path))) {
(Some(a), Some(b)) => a == b,
_ => false,
}
}
fn strip(mut t: toml::Table) -> toml::Table {
for keys in NEUTRAL {
match keys {
[k] => {
t.remove(*k);
}
[s, k] => {
if let Some(toml::Value::Table(sub)) = t.get_mut(*s) {
sub.remove(*k);
}
}
_ => {}
}
}
t
}
fn say_full_run(ctx: &Ctx, path: &str) {
static ONCE: std::sync::Once = std::sync::Once::new();
if ctx.opts.run_commands {
ONCE.call_once(|| eprintln!("spec: full verify run: {path} changed and matches [verify] full_run_on"));
}
}