use crate::grammar::GrammarState;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PolicyDecision {
#[default]
Silent,
Review,
Escalate,
}
impl PolicyDecision {
#[inline]
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Silent => "Silent",
Self::Review => "Review",
Self::Escalate => "Escalate",
}
}
#[inline]
#[must_use]
pub const fn from_grammar(state: GrammarState) -> Self {
match state {
GrammarState::Admissible => Self::Silent,
GrammarState::Boundary(_) => Self::Review,
GrammarState::Violation => Self::Escalate,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::grammar::{GrammarState, ReasonCode};
#[test]
fn admissible_maps_to_silent() {
assert_eq!(PolicyDecision::from_grammar(GrammarState::Admissible), PolicyDecision::Silent);
}
#[test]
fn boundary_maps_to_review_regardless_of_reason() {
for r in [
ReasonCode::SustainedOutwardDrift,
ReasonCode::AbruptSlewViolation,
ReasonCode::RecurrentBoundaryGrazing,
ReasonCode::EnvelopeViolation,
] {
assert_eq!(PolicyDecision::from_grammar(GrammarState::Boundary(r)), PolicyDecision::Review);
}
}
#[test]
fn violation_maps_to_escalate() {
assert_eq!(PolicyDecision::from_grammar(GrammarState::Violation), PolicyDecision::Escalate);
}
#[test]
fn labels_are_canonical() {
assert_eq!(PolicyDecision::Silent.label(), "Silent");
assert_eq!(PolicyDecision::Review.label(), "Review");
assert_eq!(PolicyDecision::Escalate.label(), "Escalate");
}
}