use super::context::EvaluationContext;
use super::evaluator::{ConditionEvaluator, ConditionResult};
use crate::expr::ConditionExpr;
pub struct ConditionExprEvaluator<'a, E: ConditionEvaluator> {
evaluator: &'a E,
}
impl<'a, E: ConditionEvaluator> ConditionExprEvaluator<'a, E> {
pub fn new(evaluator: &'a E) -> Self {
Self { evaluator }
}
pub fn evaluator(&self) -> &'a E {
self.evaluator
}
pub fn evaluate(&self, expr: &ConditionExpr, ctx: &EvaluationContext) -> ConditionResult {
match expr {
ConditionExpr::Ref(id) => self.evaluator.evaluate(*id, ctx),
ConditionExpr::And(exprs) => self.evaluate_and(exprs, ctx),
ConditionExpr::Or(exprs) => self.evaluate_or(exprs, ctx),
ConditionExpr::Xor(left, right) => {
let l = self.evaluate(left, ctx);
let r = self.evaluate(right, ctx);
self.evaluate_xor(l, r)
}
ConditionExpr::Not(inner) => {
let result = self.evaluate(inner, ctx);
self.evaluate_not(result)
}
ConditionExpr::Package { .. } => {
ConditionResult::True
}
}
}
fn evaluate_and(&self, exprs: &[ConditionExpr], ctx: &EvaluationContext) -> ConditionResult {
let mut has_unknown = false;
for expr in exprs {
match self.evaluate(expr, ctx) {
ConditionResult::False => return ConditionResult::False,
ConditionResult::Unknown => has_unknown = true,
ConditionResult::True => {}
}
}
if has_unknown {
ConditionResult::Unknown
} else {
ConditionResult::True
}
}
fn evaluate_or(&self, exprs: &[ConditionExpr], ctx: &EvaluationContext) -> ConditionResult {
let mut has_unknown = false;
for expr in exprs {
match self.evaluate(expr, ctx) {
ConditionResult::True => return ConditionResult::True,
ConditionResult::Unknown => has_unknown = true,
ConditionResult::False => {}
}
}
if has_unknown {
ConditionResult::Unknown
} else {
ConditionResult::False
}
}
fn evaluate_xor(&self, left: ConditionResult, right: ConditionResult) -> ConditionResult {
match (left, right) {
(ConditionResult::True, ConditionResult::False)
| (ConditionResult::False, ConditionResult::True) => ConditionResult::True,
(ConditionResult::True, ConditionResult::True)
| (ConditionResult::False, ConditionResult::False) => ConditionResult::False,
_ => ConditionResult::Unknown,
}
}
fn evaluate_not(&self, result: ConditionResult) -> ConditionResult {
match result {
ConditionResult::True => ConditionResult::False,
ConditionResult::False => ConditionResult::True,
ConditionResult::Unknown => ConditionResult::Unknown,
}
}
pub fn evaluate_status(&self, ahb_status: &str, ctx: &EvaluationContext) -> ConditionResult {
use crate::expr::ConditionParser;
match ConditionParser::parse(ahb_status) {
Ok(Some(expr)) => self.evaluate(&expr, ctx),
Ok(None) => ConditionResult::True, Err(_) => ConditionResult::Unknown, }
}
pub fn evaluate_status_with_ub(
&self,
ahb_status: &str,
ctx: &EvaluationContext,
ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
) -> ConditionResult {
use crate::expr::ConditionParser;
match ConditionParser::parse_with_ub(ahb_status, ub_definitions) {
Ok(Some(expr)) => self.evaluate(&expr, ctx),
Ok(None) => ConditionResult::True,
Err(_) => ConditionResult::Unknown,
}
}
pub fn evaluate_status_detailed(
&self,
ahb_status: &str,
ctx: &EvaluationContext,
) -> (ConditionResult, Vec<u32>) {
use crate::expr::ConditionParser;
match ConditionParser::parse(ahb_status) {
Ok(Some(expr)) => {
let result = self.evaluate(&expr, ctx);
if result.is_unknown() {
let unknown_ids = self.collect_unknown_ids(&expr, ctx);
(result, unknown_ids)
} else {
(result, Vec::new())
}
}
Ok(None) => (ConditionResult::True, Vec::new()),
Err(_) => (ConditionResult::Unknown, Vec::new()),
}
}
pub fn evaluate_status_detailed_with_ub(
&self,
ahb_status: &str,
ctx: &EvaluationContext,
ub_definitions: &std::collections::BTreeMap<String, crate::expr::ConditionExpr>,
) -> (ConditionResult, Vec<u32>) {
use crate::expr::ConditionParser;
match ConditionParser::parse_with_ub(ahb_status, ub_definitions) {
Ok(Some(expr)) => {
let result = self.evaluate(&expr, ctx);
if result.is_unknown() {
let unknown_ids = self.collect_unknown_ids(&expr, ctx);
(result, unknown_ids)
} else {
(result, Vec::new())
}
}
Ok(None) => (ConditionResult::True, Vec::new()),
Err(_) => (ConditionResult::Unknown, Vec::new()),
}
}
fn collect_unknown_ids(&self, expr: &ConditionExpr, ctx: &EvaluationContext) -> Vec<u32> {
let mut ids = Vec::new();
self.collect_unknown_ids_inner(expr, ctx, &mut ids);
ids
}
fn collect_unknown_ids_inner(
&self,
expr: &ConditionExpr,
ctx: &EvaluationContext,
ids: &mut Vec<u32>,
) {
match expr {
ConditionExpr::Ref(id) => {
if self.evaluator.evaluate(*id, ctx).is_unknown() {
ids.push(*id);
}
}
ConditionExpr::And(exprs) | ConditionExpr::Or(exprs) => {
for e in exprs {
self.collect_unknown_ids_inner(e, ctx, ids);
}
}
ConditionExpr::Xor(left, right) => {
self.collect_unknown_ids_inner(left, ctx, ids);
self.collect_unknown_ids_inner(right, ctx, ids);
}
ConditionExpr::Not(inner) => {
self.collect_unknown_ids_inner(inner, ctx, ids);
}
ConditionExpr::Package { .. } => {
}
}
}
}
#[cfg(test)]
mod tests {
use super::super::evaluator::{ConditionResult as CR, NoOpExternalProvider};
use super::*;
use mig_types::segment::OwnedSegment;
use std::collections::HashMap;
struct MockEvaluator {
results: HashMap<u32, ConditionResult>,
external_ids: Vec<u32>,
}
impl MockEvaluator {
fn new() -> Self {
Self {
results: HashMap::new(),
external_ids: Vec::new(),
}
}
fn with_condition(mut self, id: u32, result: ConditionResult) -> Self {
self.results.insert(id, result);
self
}
}
impl ConditionEvaluator for MockEvaluator {
fn evaluate(&self, condition: u32, _ctx: &EvaluationContext) -> ConditionResult {
self.results
.get(&condition)
.copied()
.unwrap_or(ConditionResult::Unknown)
}
fn is_external(&self, condition: u32) -> bool {
self.external_ids.contains(&condition)
}
fn message_type(&self) -> &str {
"TEST"
}
fn format_version(&self) -> &str {
"FV_TEST"
}
}
fn empty_context() -> (NoOpExternalProvider, Vec<OwnedSegment>) {
(NoOpExternalProvider, Vec::new())
}
fn make_ctx<'a>(
external: &'a NoOpExternalProvider,
segments: &'a [OwnedSegment],
) -> EvaluationContext<'a> {
EvaluationContext::new("11001", external, segments)
}
#[test]
fn test_eval_single_true() {
let eval = MockEvaluator::new().with_condition(1, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::True);
}
#[test]
fn test_eval_single_false() {
let eval = MockEvaluator::new().with_condition(1, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
assert_eq!(expr_eval.evaluate(&ConditionExpr::Ref(1), &ctx), CR::False);
}
#[test]
fn test_eval_single_unknown() {
let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
assert_eq!(
expr_eval.evaluate(&ConditionExpr::Ref(999), &ctx),
CR::Unknown
);
}
#[test]
fn test_eval_and_both_true() {
let eval = MockEvaluator::new()
.with_condition(1, CR::True)
.with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_eval_and_one_false_short_circuits() {
let eval = MockEvaluator::new()
.with_condition(1, CR::False)
.with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_eval_and_one_unknown_true_gives_unknown() {
let eval = MockEvaluator::new()
.with_condition(1, CR::True)
.with_condition(2, CR::Unknown);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
}
#[test]
fn test_eval_and_false_beats_unknown() {
let eval = MockEvaluator::new()
.with_condition(1, CR::False)
.with_condition(2, CR::Unknown);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_eval_and_three_way() {
let eval = MockEvaluator::new()
.with_condition(182, CR::True)
.with_condition(6, CR::True)
.with_condition(570, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![
ConditionExpr::Ref(182),
ConditionExpr::Ref(6),
ConditionExpr::Ref(570),
]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_eval_and_three_way_one_false() {
let eval = MockEvaluator::new()
.with_condition(182, CR::True)
.with_condition(6, CR::True)
.with_condition(570, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![
ConditionExpr::Ref(182),
ConditionExpr::Ref(6),
ConditionExpr::Ref(570),
]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_eval_or_both_false() {
let eval = MockEvaluator::new()
.with_condition(1, CR::False)
.with_condition(2, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_eval_or_one_true_short_circuits() {
let eval = MockEvaluator::new()
.with_condition(1, CR::False)
.with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_eval_or_true_beats_unknown() {
let eval = MockEvaluator::new()
.with_condition(1, CR::Unknown)
.with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_eval_or_false_and_unknown_gives_unknown() {
let eval = MockEvaluator::new()
.with_condition(1, CR::False)
.with_condition(2, CR::Unknown);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
}
#[test]
fn test_eval_xor_true_false() {
let eval = MockEvaluator::new()
.with_condition(1, CR::True)
.with_condition(2, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::Ref(1)),
Box::new(ConditionExpr::Ref(2)),
);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_eval_xor_both_true() {
let eval = MockEvaluator::new()
.with_condition(1, CR::True)
.with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::Ref(1)),
Box::new(ConditionExpr::Ref(2)),
);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_eval_xor_both_false() {
let eval = MockEvaluator::new()
.with_condition(1, CR::False)
.with_condition(2, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::Ref(1)),
Box::new(ConditionExpr::Ref(2)),
);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_eval_xor_unknown_propagates() {
let eval = MockEvaluator::new()
.with_condition(1, CR::True)
.with_condition(2, CR::Unknown);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::Ref(1)),
Box::new(ConditionExpr::Ref(2)),
);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
}
#[test]
fn test_eval_not_true() {
let eval = MockEvaluator::new().with_condition(1, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_eval_not_false() {
let eval = MockEvaluator::new().with_condition(1, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_eval_not_unknown() {
let eval = MockEvaluator::new(); let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Not(Box::new(ConditionExpr::Ref(1)));
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
}
#[test]
fn test_eval_complex_nested() {
let eval = MockEvaluator::new()
.with_condition(1, CR::True)
.with_condition(2, CR::False)
.with_condition(3, CR::True)
.with_condition(4, CR::True)
.with_condition(5, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![
ConditionExpr::Or(vec![
ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]),
ConditionExpr::And(vec![ConditionExpr::Ref(3), ConditionExpr::Ref(4)]),
]),
ConditionExpr::Ref(5),
]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_eval_xor_with_nested_and() {
let eval = MockEvaluator::new()
.with_condition(102, CR::True)
.with_condition(2006, CR::True)
.with_condition(103, CR::False)
.with_condition(2005, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Xor(
Box::new(ConditionExpr::And(vec![
ConditionExpr::Ref(102),
ConditionExpr::Ref(2006),
])),
Box::new(ConditionExpr::And(vec![
ConditionExpr::Ref(103),
ConditionExpr::Ref(2005),
])),
);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_evaluate_status_with_conditions() {
let eval = MockEvaluator::new()
.with_condition(182, CR::True)
.with_condition(152, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
assert_eq!(
expr_eval.evaluate_status("Muss [182] ∧ [152]", &ctx),
CR::True
);
}
#[test]
fn test_evaluate_status_no_conditions() {
let eval = MockEvaluator::new();
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
assert_eq!(expr_eval.evaluate_status("Muss", &ctx), CR::True);
}
#[test]
fn test_evaluate_status_empty() {
let eval = MockEvaluator::new();
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
assert_eq!(expr_eval.evaluate_status("", &ctx), CR::True);
}
#[test]
fn test_unknown_propagation_and_or_mix() {
let eval = MockEvaluator::new().with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Or(vec![
ConditionExpr::Ref(1),
ConditionExpr::And(vec![ConditionExpr::Ref(2), ConditionExpr::Ref(3)]),
]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::Unknown);
}
#[test]
fn test_or_short_circuits_past_unknown() {
let eval = MockEvaluator::new().with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::Or(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::True);
}
#[test]
fn test_and_short_circuits_past_unknown() {
let eval = MockEvaluator::new().with_condition(2, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let expr = ConditionExpr::And(vec![ConditionExpr::Ref(1), ConditionExpr::Ref(2)]);
assert_eq!(expr_eval.evaluate(&expr, &ctx), CR::False);
}
#[test]
fn test_detailed_returns_unknown_ids() {
let eval = MockEvaluator::new().with_condition(182, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
assert_eq!(result, CR::Unknown);
assert_eq!(unknown_ids, vec![8]);
}
#[test]
fn test_detailed_multiple_unknown_ids() {
let eval = MockEvaluator::new().with_condition(2, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let (result, unknown_ids) =
expr_eval.evaluate_status_detailed("Muss [1] ∧ [2] ∧ [3]", &ctx);
assert_eq!(result, CR::Unknown);
assert_eq!(unknown_ids, vec![1, 3]);
}
#[test]
fn test_detailed_no_unknown_when_true() {
let eval = MockEvaluator::new()
.with_condition(182, CR::True)
.with_condition(152, CR::True);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [152]", &ctx);
assert_eq!(result, CR::True);
assert!(unknown_ids.is_empty());
}
#[test]
fn test_detailed_no_unknown_when_false() {
let eval = MockEvaluator::new().with_condition(182, CR::False);
let (ext, segs) = empty_context();
let ctx = make_ctx(&ext, &segs);
let expr_eval = ConditionExprEvaluator::new(&eval);
let (result, unknown_ids) = expr_eval.evaluate_status_detailed("Muss [182] ∧ [8]", &ctx);
assert_eq!(result, CR::False);
assert!(unknown_ids.is_empty());
}
}