Coverage for flowr / domain / condition.py: 93%
45 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-22 18:42 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-22 18:42 +0000
1"""Condition evaluation for flow definition guard conditions."""
3import re
4from enum import Enum
7class ConditionOperator(Enum):
8 """Operators for guard condition expressions."""
10 EQUALS = "=="
11 NOT_EQUALS = "!="
12 GREATER_THAN_OR_EQUAL = ">="
13 LESS_THAN_OR_EQUAL = "<="
14 GREATER_THAN = ">"
15 LESS_THAN = "<"
18_OPERATOR_PREFIXES: list[tuple[str, ConditionOperator]] = [
19 (">=", ConditionOperator.GREATER_THAN_OR_EQUAL),
20 ("<=", ConditionOperator.LESS_THAN_OR_EQUAL),
21 ("==", ConditionOperator.EQUALS),
22 ("!=", ConditionOperator.NOT_EQUALS),
23 (">", ConditionOperator.GREATER_THAN),
24 ("<", ConditionOperator.LESS_THAN),
25]
28def _extract_numeric(s: str) -> float | None:
29 """Extract the first numeric value from a string."""
30 match = re.search(r"[-+]?\d*\.?\d+", s)
31 if match is None:
32 return None
33 return float(match.group())
36def parse_condition(condition_str: str) -> tuple[ConditionOperator, str]:
37 """Parse a condition string into an operator and value."""
38 for prefix, op in _OPERATOR_PREFIXES:
39 if condition_str.startswith(prefix):
40 return op, condition_str[len(prefix) :]
41 return ConditionOperator.EQUALS, condition_str
44def _compare_numeric(
45 condition_value: str,
46 evidence_value: str,
47 op: ConditionOperator,
48) -> bool | None:
49 """Compare two values numerically. Returns None if not numeric."""
50 c_num = _extract_numeric(condition_value)
51 e_num = _extract_numeric(evidence_value)
52 if c_num is None or e_num is None:
53 return None
54 match op:
55 case ConditionOperator.GREATER_THAN_OR_EQUAL:
56 return e_num >= c_num
57 case ConditionOperator.LESS_THAN_OR_EQUAL:
58 return e_num <= c_num
59 case ConditionOperator.GREATER_THAN:
60 return e_num > c_num
61 case ConditionOperator.LESS_THAN:
62 return e_num < c_num
64 return None # pragma: no cover
67def evaluate_condition(
68 operator: ConditionOperator,
69 condition_value: str,
70 evidence_value: str,
71) -> bool:
72 """Evaluate a condition expression against an evidence value."""
73 match operator:
74 case ConditionOperator.EQUALS:
75 return evidence_value == condition_value
76 case ConditionOperator.NOT_EQUALS:
77 return evidence_value != condition_value
78 case _:
79 result = _compare_numeric(condition_value, evidence_value, operator)
80 if result is None:
81 return False
82 return result