use crate::domain::rule_id::RuleId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Sentinel {
pub rule: RuleId,
pub source: &'static str,
pub destination: &'static str,
pub declares: &'static str,
}
pub const SENTINELS: &[Sentinel] = &[
Sentinel {
rule: RuleId::RecordedDimensionOnlyShrinks,
source: "_docs/specs/SPEC-budget-debt.md",
destination: "{docs_root}/specs/SPEC-budget-debt.md",
declares: ".spec-driven-docs/debt.yaml records budget debt",
},
Sentinel {
rule: RuleId::ProjectSelectsOneSource,
source: "_docs/specs/SPEC-writing-policy.md",
destination: "{docs_root}/specs/SPEC-writing-policy.md",
declares: ".spec-driven-docs/config.yaml selects a writing style other than builtin",
},
];
#[must_use]
pub fn sentinel(rule: RuleId) -> Option<&'static Sentinel> {
SENTINELS.iter().find(|s| s.rule == rule)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_sentinel_is_owned_by_an_embedded_adopted_specification() {
for s in SENTINELS {
assert!(
crate::embedded::asset(s.source).is_some(),
"{} is not embedded",
s.source
);
let adopted = crate::domain::profile::ProfileId::KnowledgeBase
.profile()
.adopted
.iter()
.any(|p| p.source == s.source && p.destination == s.destination);
assert!(adopted, "{} is not an adopted projection", s.source);
let text = crate::embedded::asset(s.source)
.and_then(|bytes| std::str::from_utf8(bytes).ok())
.unwrap_or_default();
assert!(
crate::embedded::rule_ids_in(text).any(|id| id == s.rule.as_str()),
"{} does not define {}",
s.source,
s.rule
);
}
}
#[test]
fn no_delivered_gate_cites_a_sentinel() {
for row in crate::gates::GATES {
for rule in row.cites {
assert!(
sentinel(*rule).is_none(),
"{} cites the sentinel {rule}; an instance upgraded after the binary would fail every commit",
row.id
);
}
}
}
}