use super::operations::{OperationResult, VetoType};
use crate::planning::execution_plan::JumpVetoSemantics;
use crate::planning::semantics::ValueKind;
#[derive(Debug, Clone)]
pub(crate) enum BranchOutcome {
Taken,
NotTaken,
Propagate(OperationResult),
}
fn boolean_value(result: &OperationResult, context: &str) -> bool {
match result {
OperationResult::Value(literal) => match &literal.value {
ValueKind::Boolean(boolean) => *boolean,
other => panic!("BUG: {context} expected a boolean, got {other:?}"),
},
OperationResult::Veto(veto) => {
panic!("BUG: {context} inspected for a boolean while vetoed: {veto}")
}
}
}
pub(crate) fn unless_condition_outcome(
condition: &OperationResult,
veto_semantics: JumpVetoSemantics,
) -> BranchOutcome {
match veto_semantics {
JumpVetoSemantics::UnlessRuleReference => {
if condition.vetoed() {
return BranchOutcome::Propagate(condition.clone());
}
}
JumpVetoSemantics::UnlessExpression => {
if matches!(
condition,
OperationResult::Veto(VetoType::MissingData { .. })
) {
return BranchOutcome::Propagate(condition.clone());
}
if condition.vetoed() {
return BranchOutcome::NotTaken;
}
}
}
if boolean_value(condition, "unless condition") {
BranchOutcome::Taken
} else {
BranchOutcome::NotTaken
}
}
#[cfg(test)]
pub(crate) fn and_conjunct_outcome(conjunct: &OperationResult) -> BranchOutcome {
if conjunct.vetoed() {
return BranchOutcome::Propagate(conjunct.clone());
}
if boolean_value(conjunct, "and conjunct") {
BranchOutcome::Taken
} else {
BranchOutcome::NotTaken
}
}
#[cfg(test)]
pub(crate) fn or_disjunct_outcome(
disjunct: &OperationResult,
is_last_disjunct: bool,
) -> BranchOutcome {
if is_last_disjunct {
return BranchOutcome::Propagate(disjunct.clone());
}
if matches!(
disjunct,
OperationResult::Veto(VetoType::MissingData { .. })
) {
return BranchOutcome::Propagate(disjunct.clone());
}
if disjunct.vetoed() {
return BranchOutcome::NotTaken;
}
if boolean_value(disjunct, "or disjunct") {
BranchOutcome::Taken
} else {
BranchOutcome::NotTaken
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::planning::semantics::{DataPath, LiteralValue};
fn boolean(value: bool) -> OperationResult {
OperationResult::Value(LiteralValue::from_bool(value))
}
fn user_veto() -> OperationResult {
OperationResult::Veto(VetoType::UserDefined {
message: Some("blocked".to_string()),
})
}
fn missing_data_veto() -> OperationResult {
OperationResult::Veto(VetoType::MissingData {
data: DataPath::new(vec![], "x".to_string()),
})
}
#[test]
fn unless_rule_reference_propagates_any_veto() {
assert!(matches!(
unless_condition_outcome(&user_veto(), JumpVetoSemantics::UnlessRuleReference),
BranchOutcome::Propagate(_)
));
assert!(matches!(
unless_condition_outcome(&missing_data_veto(), JumpVetoSemantics::UnlessRuleReference),
BranchOutcome::Propagate(_)
));
assert!(matches!(
unless_condition_outcome(&boolean(true), JumpVetoSemantics::UnlessRuleReference),
BranchOutcome::Taken
));
assert!(matches!(
unless_condition_outcome(&boolean(false), JumpVetoSemantics::UnlessRuleReference),
BranchOutcome::NotTaken
));
}
#[test]
fn unless_expression_propagates_only_missing_data() {
assert!(matches!(
unless_condition_outcome(&missing_data_veto(), JumpVetoSemantics::UnlessExpression),
BranchOutcome::Propagate(_)
));
assert!(matches!(
unless_condition_outcome(&user_veto(), JumpVetoSemantics::UnlessExpression),
BranchOutcome::NotTaken
));
}
#[test]
fn and_conjunct_propagates_every_veto() {
assert!(matches!(
and_conjunct_outcome(&user_veto()),
BranchOutcome::Propagate(_)
));
assert!(matches!(
and_conjunct_outcome(&missing_data_veto()),
BranchOutcome::Propagate(_)
));
assert!(matches!(
and_conjunct_outcome(&boolean(true)),
BranchOutcome::Taken
));
assert!(matches!(
and_conjunct_outcome(&boolean(false)),
BranchOutcome::NotTaken
));
}
#[test]
fn or_disjunct_treats_non_missing_vetoes_as_non_match_when_not_last() {
assert!(matches!(
or_disjunct_outcome(&user_veto(), false),
BranchOutcome::NotTaken
));
assert!(matches!(
or_disjunct_outcome(&missing_data_veto(), false),
BranchOutcome::Propagate(_)
));
assert!(matches!(
or_disjunct_outcome(&boolean(true), false),
BranchOutcome::Taken
));
assert!(matches!(
or_disjunct_outcome(&boolean(false), false),
BranchOutcome::NotTaken
));
}
#[test]
fn or_last_disjunct_always_propagates_its_result() {
assert!(matches!(
or_disjunct_outcome(&user_veto(), true),
BranchOutcome::Propagate(_)
));
assert!(matches!(
or_disjunct_outcome(&boolean(false), true),
BranchOutcome::Propagate(_)
));
}
}