use std::sync::LazyLock;
use crate::domain::projection::SentinelDeclaration;
use crate::domain::rule_id::RuleId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Sentinel {
pub rule: RuleId,
pub source: String,
pub destination: String,
pub declares: String,
}
pub static SENTINELS: LazyLock<Vec<Sentinel>> =
LazyLock::new(|| from_declaration(&crate::domain::profile::DECLARATION.sentinels));
#[must_use]
pub fn from_declaration(declared: &[SentinelDeclaration]) -> Vec<Sentinel> {
declared
.iter()
.filter_map(|entry| {
let rule = RuleId::ALL
.iter()
.copied()
.find(|known| known.as_str() == entry.rule)?;
Some(Sentinel {
rule,
source: entry.source.clone(),
destination: entry.destination.clone(),
declares: entry.declares.clone(),
})
})
.collect()
}
#[must_use]
pub fn sentinel(rule: RuleId) -> Option<&'static Sentinel> {
SENTINELS.iter().find(|held| held.rule == rule)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_sentinel_is_owned_by_an_embedded_adopted_specification() {
for s in SENTINELS.iter() {
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
);
}
}
}
}