use crate::{ClauseLabel, ObligationId, Policy, PreparedPolicy};
use std::collections::BTreeSet;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PolicyAdvice<'a> {
UnlabeledGrant,
DuplicateLabel(&'a ClauseLabel),
MissingReason,
}
#[derive(Clone, Debug)]
pub struct PolicyInspection<'a> {
pub labels: BTreeSet<&'a ClauseLabel>,
pub reasons: BTreeSet<&'a str>,
pub obligations: BTreeSet<&'a ObligationId>,
pub advice: Vec<PolicyAdvice<'a>>,
}
impl<O> PreparedPolicy<O> {
#[must_use]
pub fn inspect(&self) -> PolicyInspection<'_> {
let mut inspection = PolicyInspection {
labels: BTreeSet::new(),
reasons: BTreeSet::new(),
obligations: BTreeSet::new(),
advice: Vec::new(),
};
let mut pending = vec![self.policy()];
while let Some(policy) = pending.pop() {
match policy {
Policy::Grant {
label,
reason,
obligations,
..
} => inspection.grant(
label.as_ref(),
reason.as_ref().map(crate::ReasonCode::as_str),
obligations,
),
Policy::All(children) | Policy::Any(children) => {
pending.extend(children.iter().rev());
}
Policy::OrElse { primary, fallback } => {
pending.push(fallback);
pending.push(primary);
}
Policy::Permit(_) | Policy::Deny => {}
}
}
inspection
}
}
impl<'a> PolicyInspection<'a> {
fn grant(
&mut self,
label: Option<&'a ClauseLabel>,
reason: Option<&'a str>,
obligations: &'a [ObligationId],
) {
match label {
None => self.advice.push(PolicyAdvice::UnlabeledGrant),
Some(label) if !self.labels.insert(label) => {
self.advice.push(PolicyAdvice::DuplicateLabel(label));
}
Some(_) => {}
}
match reason.or_else(|| label.map(ClauseLabel::as_str)) {
Some(reason) => {
self.reasons.insert(reason);
}
None => self.advice.push(PolicyAdvice::MissingReason),
}
self.obligations.extend(obligations);
}
}