rust_rule_engine/rete/
alpha.rs1use super::facts::{FactValue, TypedFacts};
4
5#[derive(Debug, Clone)]
6pub struct AlphaNode {
7 pub field: String,
8 pub operator: String,
9 pub value: String,
10}
11
12
13impl AlphaNode {
14 pub fn matches(&self, fact_field: &str, fact_value: &str) -> bool {
16 if self.field != fact_field {
17 return false;
18 }
19 match self.operator.as_str() {
20 "==" => fact_value == self.value,
21 "!=" => fact_value != self.value,
22 ">" => parse_num(fact_value) > parse_num(&self.value),
23 "<" => parse_num(fact_value) < parse_num(&self.value),
24 ">=" => parse_num(fact_value) >= parse_num(&self.value),
25 "<=" => parse_num(fact_value) <= parse_num(&self.value),
26 "contains" => fact_value.contains(&self.value),
27 "startsWith" => fact_value.starts_with(&self.value),
28 "endsWith" => fact_value.ends_with(&self.value),
29 "matches" => wildcard_match(fact_value, &self.value),
30 _ => false,
31 }
32 }
33
34 pub fn matches_typed(&self, facts: &TypedFacts) -> bool {
36 let expected_value = if let Some(var_value) = facts.get(&self.value) {
39 var_value.clone()
41 } else {
42 self.parse_value_string(&self.value)
44 };
45
46 facts.evaluate_condition(&self.field, &self.operator, &expected_value)
47 }
48
49 fn parse_value_string(&self, s: &str) -> FactValue {
51 if let Ok(i) = s.parse::<i64>() {
53 FactValue::Integer(i)
54 } else if let Ok(f) = s.parse::<f64>() {
55 FactValue::Float(f)
56 } else if let Ok(b) = s.parse::<bool>() {
57 FactValue::Boolean(b)
58 } else if s == "null" {
59 FactValue::Null
60 } else {
61 FactValue::String(s.to_string())
62 }
63 }
64
65 pub fn with_typed_value(field: String, operator: String, value: FactValue) -> Self {
67 Self {
68 field,
69 operator,
70 value: value.as_string(),
71 }
72 }
73}
74
75fn parse_num(s: &str) -> f64 {
76 s.parse::<f64>().unwrap_or(0.0)
77}
78
79fn wildcard_match(text: &str, pattern: &str) -> bool {
81 let text_chars: Vec<char> = text.chars().collect();
82 let pattern_chars: Vec<char> = pattern.chars().collect();
83 wildcard_match_impl(&text_chars, &pattern_chars, 0, 0)
84}
85
86fn wildcard_match_impl(text: &[char], pattern: &[char], ti: usize, pi: usize) -> bool {
87 if pi == pattern.len() {
88 return ti == text.len();
89 }
90
91 if pattern[pi] == '*' {
92 for i in ti..=text.len() {
93 if wildcard_match_impl(text, pattern, i, pi + 1) {
94 return true;
95 }
96 }
97 false
98 } else if ti < text.len() && (pattern[pi] == '?' || pattern[pi] == text[ti]) {
99 wildcard_match_impl(text, pattern, ti + 1, pi + 1)
100 } else {
101 false
102 }
103}