use super::compiler::CompiledPlan;
use super::model::{InputProvider, Predicate, RuleDecision, Selector, Value};
#[derive(Debug, Clone)]
pub struct RuleOutcome {
pub rule_id: String,
pub decision: RuleDecision,
}
#[derive(Debug, Clone)]
pub struct RuntimeResult {
pub outcomes: Vec<RuleOutcome>,
pub selectors_scanned: usize,
pub allow: bool,
}
#[derive(Debug, Clone)]
pub struct RuntimeState {
decisions: Vec<RuleDecision>,
pending_required: usize,
selectors_scanned: usize,
}
impl RuntimeState {
pub fn new(plan: &CompiledPlan) -> Self {
Self {
decisions: vec![RuleDecision::Unresolved; plan.rules.len()],
pending_required: plan.required_count,
selectors_scanned: 0,
}
}
pub fn apply(&mut self, plan: &CompiledPlan, selector: &Selector, value: &Value) {
self.selectors_scanned += 1;
if let Some(rule_indexes) = plan.path_index.get(selector) {
for &rule_idx in rule_indexes {
if self.decisions[rule_idx] != RuleDecision::Unresolved {
continue;
}
let rule = &plan.rules[rule_idx];
let ok = predicate_matches(value, &rule.predicate);
self.decisions[rule_idx] = if ok {
RuleDecision::True
} else {
RuleDecision::False
};
if rule.required {
self.pending_required = self.pending_required.saturating_sub(1);
}
}
}
}
pub fn should_terminate(&self) -> bool {
self.pending_required == 0
}
pub fn finalize(mut self, plan: &CompiledPlan) -> RuntimeResult {
for (idx, rule) in plan.rules.iter().enumerate() {
if rule.required && self.decisions[idx] == RuleDecision::Unresolved {
self.decisions[idx] = RuleDecision::False;
}
}
let allow = plan
.rules
.iter()
.enumerate()
.filter(|(_, r)| r.required)
.all(|(i, _)| self.decisions[i] == RuleDecision::True);
let outcomes = plan
.rules
.iter()
.enumerate()
.map(|(idx, rule)| RuleOutcome {
rule_id: rule.id.clone(),
decision: self.decisions[idx].clone(),
})
.collect();
RuntimeResult {
outcomes,
selectors_scanned: self.selectors_scanned,
allow,
}
}
}
pub fn execute<I: InputProvider>(plan: &CompiledPlan, input: &I) -> RuntimeResult {
let mut state = RuntimeState::new(plan);
for selector in &plan.selectors {
if state.should_terminate() {
break;
}
let value = input.value_for(selector);
state.apply(plan, selector, &value);
}
state.finalize(plan)
}
fn predicate_matches(value: &Value, predicate: &Predicate) -> bool {
match (value, predicate) {
(Value::String(v), Predicate::EqString(expected)) => v == expected,
(Value::Bool(v), Predicate::BoolIsTrue) => *v,
(Value::U64(v), Predicate::MaxU64(limit)) => v <= limit,
(Value::Strings(values), Predicate::ContainsString(required)) => {
values.iter().any(|v| v == required)
}
(Value::U64(v), Predicate::MinU64(limit)) => v >= limit,
(Value::Missing, Predicate::Exists) => false,
(_, Predicate::Exists) => true,
(Value::String(v), Predicate::InSet(set)) => set.contains(v),
_ => false,
}
}