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 = self.parse_value_string(&self.value);
38 facts.evaluate_condition(&self.field, &self.operator, &expected_value)
39 }
40
41 fn parse_value_string(&self, s: &str) -> FactValue {
43 if let Ok(i) = s.parse::<i64>() {
45 FactValue::Integer(i)
46 } else if let Ok(f) = s.parse::<f64>() {
47 FactValue::Float(f)
48 } else if let Ok(b) = s.parse::<bool>() {
49 FactValue::Boolean(b)
50 } else if s == "null" {
51 FactValue::Null
52 } else {
53 FactValue::String(s.to_string())
54 }
55 }
56
57 pub fn with_typed_value(field: String, operator: String, value: FactValue) -> Self {
59 Self {
60 field,
61 operator,
62 value: value.as_string(),
63 }
64 }
65}
66
67fn parse_num(s: &str) -> f64 {
68 s.parse::<f64>().unwrap_or(0.0)
69}
70
71fn wildcard_match(text: &str, pattern: &str) -> bool {
73 let text_chars: Vec<char> = text.chars().collect();
74 let pattern_chars: Vec<char> = pattern.chars().collect();
75 wildcard_match_impl(&text_chars, &pattern_chars, 0, 0)
76}
77
78fn wildcard_match_impl(text: &[char], pattern: &[char], ti: usize, pi: usize) -> bool {
79 if pi == pattern.len() {
80 return ti == text.len();
81 }
82
83 if pattern[pi] == '*' {
84 for i in ti..=text.len() {
85 if wildcard_match_impl(text, pattern, i, pi + 1) {
86 return true;
87 }
88 }
89 false
90 } else if ti < text.len() && (pattern[pi] == '?' || pattern[pi] == text[ti]) {
91 wildcard_match_impl(text, pattern, ti + 1, pi + 1)
92 } else {
93 false
94 }
95}