rust_rule_engine/engine/
pattern_matcher.rs1use crate::engine::facts::Facts;
2use crate::engine::rule::ConditionGroup;
3use std::collections::HashMap;
4
5pub struct PatternMatcher;
7
8impl PatternMatcher {
9 pub fn evaluate_exists(condition: &ConditionGroup, facts: &Facts) -> bool {
11 let all_facts = facts.get_all_facts();
12
13 for (fact_name, fact_value) in &all_facts {
16 if let Some(target_type) = Self::extract_target_type(condition) {
18 if fact_name.starts_with(&target_type) {
20 let mut temp_facts = HashMap::new();
23 temp_facts.insert(target_type.clone(), fact_value.clone());
24
25 if condition.evaluate(&temp_facts) {
27 return true;
28 }
29 }
30 } else {
31 if condition.evaluate(&all_facts) {
33 return true;
34 }
35 }
36 }
37
38 false
39 }
40
41 pub fn evaluate_not(condition: &ConditionGroup, facts: &Facts) -> bool {
43 !Self::evaluate_exists(condition, facts)
45 }
46
47 pub fn evaluate_forall(condition: &ConditionGroup, facts: &Facts) -> bool {
49 let all_facts = facts.get_all_facts();
50
51 let target_type = match Self::extract_target_type(condition) {
53 Some(t) => t,
54 None => {
55 return condition.evaluate(&all_facts);
57 }
58 };
59
60 let mut target_facts = Vec::new();
62 for (fact_name, fact_value) in &all_facts {
63 if fact_name.starts_with(&target_type) || fact_name == &target_type {
66 target_facts.push((fact_name, fact_value));
67 }
68 }
69
70 if target_facts.is_empty() {
72 return true;
73 }
74
75 for (_fact_name, fact_value) in target_facts {
77 let mut temp_facts = HashMap::new();
80 temp_facts.insert(target_type.clone(), fact_value.clone());
81
82 if !condition.evaluate(&temp_facts) {
83 return false; }
85 }
86
87 true }
89
90 fn extract_target_type(condition: &ConditionGroup) -> Option<String> {
92 match condition {
93 ConditionGroup::Single(cond) => {
94 if let Some(dot_pos) = cond.field.find('.') {
96 Some(cond.field[..dot_pos].to_string())
97 } else {
98 Some(cond.field.clone())
99 }
100 }
101 ConditionGroup::Compound { left, .. } => {
102 Self::extract_target_type(left)
104 }
105 _ => None,
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::engine::rule::Condition;
114 use crate::types::{Operator, Value};
115 use std::collections::HashMap;
116
117 #[test]
118 fn test_exists_pattern_matching() {
119 let facts = Facts::new();
120
121 let mut customer1 = HashMap::new();
123 customer1.insert("tier".to_string(), Value::String("VIP".to_string()));
124 facts
125 .add_value("Customer1", Value::Object(customer1))
126 .unwrap();
127
128 let mut customer2 = HashMap::new();
129 customer2.insert("tier".to_string(), Value::String("Regular".to_string()));
130 facts
131 .add_value("Customer2", Value::Object(customer2))
132 .unwrap();
133
134 let condition = ConditionGroup::Single(Condition::new(
136 "Customer1.tier".to_string(),
137 Operator::Equal,
138 Value::String("VIP".to_string()),
139 ));
140
141 assert!(PatternMatcher::evaluate_exists(&condition, &facts));
142
143 let condition_fail = ConditionGroup::Single(Condition::new(
145 "Customer1.tier".to_string(),
146 Operator::Equal,
147 Value::String("Premium".to_string()),
148 ));
149
150 assert!(!PatternMatcher::evaluate_exists(&condition_fail, &facts));
151 }
152
153 #[test]
154 fn test_not_pattern_matching() {
155 let facts = Facts::new();
156
157 let mut customer = HashMap::new();
159 customer.insert("tier".to_string(), Value::String("Regular".to_string()));
160 facts
161 .add_value("Customer", Value::Object(customer))
162 .unwrap();
163
164 let condition = ConditionGroup::Single(Condition::new(
166 "Customer.tier".to_string(),
167 Operator::Equal,
168 Value::String("VIP".to_string()),
169 ));
170
171 assert!(PatternMatcher::evaluate_not(&condition, &facts));
172
173 let condition_fail = ConditionGroup::Single(Condition::new(
175 "Customer.tier".to_string(),
176 Operator::Equal,
177 Value::String("Regular".to_string()),
178 ));
179
180 assert!(!PatternMatcher::evaluate_not(&condition_fail, &facts));
181 }
182
183 #[test]
184 fn test_forall_pattern_matching() {
185 let facts = Facts::new();
186
187 let mut customer1 = HashMap::new();
189 customer1.insert("tier".to_string(), Value::String("VIP".to_string()));
190 facts
191 .add_value("Customer1", Value::Object(customer1))
192 .unwrap();
193
194 let mut customer2 = HashMap::new();
195 customer2.insert("tier".to_string(), Value::String("VIP".to_string()));
196 facts
197 .add_value("Customer2", Value::Object(customer2))
198 .unwrap();
199
200 let condition = ConditionGroup::Single(Condition::new(
203 "Customer.tier".to_string(), Operator::Equal,
205 Value::String("VIP".to_string()),
206 ));
207
208 assert!(PatternMatcher::evaluate_forall(&condition, &facts));
209
210 let mut customer3 = HashMap::new();
212 customer3.insert("tier".to_string(), Value::String("Regular".to_string()));
213 facts
214 .add_value("Customer3", Value::Object(customer3))
215 .unwrap();
216
217 assert!(!PatternMatcher::evaluate_forall(&condition, &facts));
219 }
220
221 #[test]
222 fn test_extract_target_type() {
223 let condition = ConditionGroup::Single(Condition::new(
224 "Customer.tier".to_string(),
225 Operator::Equal,
226 Value::String("VIP".to_string()),
227 ));
228
229 assert_eq!(
230 PatternMatcher::extract_target_type(&condition),
231 Some("Customer".to_string())
232 );
233
234 let simple_condition = ConditionGroup::Single(Condition::new(
235 "Customer".to_string(),
236 Operator::Equal,
237 Value::String("VIP".to_string()),
238 ));
239
240 assert_eq!(
241 PatternMatcher::extract_target_type(&simple_condition),
242 Some("Customer".to_string())
243 );
244 }
245}