1use super::facts::{FactValue, TypedFacts};
10use super::multifield::MultifieldOp;
11use super::working_memory::{FactHandle, WorkingMemory};
12use std::collections::HashMap;
13
14pub type Variable = String;
16
17#[derive(Debug, Clone)]
19pub enum PatternConstraint {
20 Simple {
22 field: String,
23 operator: String,
24 value: FactValue,
25 },
26 Binding { field: String, variable: Variable },
28 Variable {
30 field: String,
31 operator: String,
32 variable: Variable,
33 },
34 MultiField {
41 field: String,
42 variable: Option<Variable>, operator: MultifieldOp,
44 value: Option<FactValue>, },
46}
47
48impl PatternConstraint {
49 pub fn simple(field: String, operator: String, value: FactValue) -> Self {
51 Self::Simple {
52 field,
53 operator,
54 value,
55 }
56 }
57
58 pub fn binding(field: String, variable: Variable) -> Self {
60 Self::Binding { field, variable }
61 }
62
63 pub fn variable(field: String, operator: String, variable: Variable) -> Self {
65 Self::Variable {
66 field,
67 operator,
68 variable,
69 }
70 }
71
72 pub fn multifield(
74 field: String,
75 operator: MultifieldOp,
76 variable: Option<Variable>,
77 value: Option<FactValue>,
78 ) -> Self {
79 Self::MultiField {
80 field,
81 operator,
82 variable,
83 value,
84 }
85 }
86
87 pub fn evaluate(
89 &self,
90 facts: &TypedFacts,
91 bindings: &HashMap<Variable, FactValue>,
92 ) -> Option<HashMap<Variable, FactValue>> {
93 match self {
94 PatternConstraint::Simple {
95 field,
96 operator,
97 value,
98 } => {
99 if facts.evaluate_condition(field, operator, value) {
100 Some(HashMap::new())
101 } else {
102 None
103 }
104 }
105 PatternConstraint::Binding { field, variable } => {
106 if let Some(fact_value) = facts.get(field) {
107 let mut new_bindings = HashMap::new();
108 new_bindings.insert(variable.clone(), fact_value.clone());
109 Some(new_bindings)
110 } else {
111 None
112 }
113 }
114 PatternConstraint::Variable {
115 field,
116 operator,
117 variable,
118 } => {
119 if let Some(bound_value) = bindings.get(variable) {
120 if facts.evaluate_condition(field, operator, bound_value) {
121 Some(HashMap::new())
122 } else {
123 None
124 }
125 } else {
126 None }
128 }
129 PatternConstraint::MultiField {
130 field,
131 operator,
132 variable,
133 value,
134 } => {
135 super::multifield::evaluate_multifield_pattern(
137 facts,
138 field,
139 operator,
140 variable.as_deref(),
141 value.as_ref(),
142 bindings,
143 )
144 }
145 }
146 }
147}
148
149#[derive(Debug, Clone)]
151pub struct Pattern {
152 pub fact_type: String,
154 pub constraints: Vec<PatternConstraint>,
156 pub name: Option<String>,
158}
159
160impl Pattern {
161 pub fn new(fact_type: String) -> Self {
163 Self {
164 fact_type,
165 constraints: Vec::new(),
166 name: None,
167 }
168 }
169
170 pub fn with_constraint(mut self, constraint: PatternConstraint) -> Self {
172 self.constraints.push(constraint);
173 self
174 }
175
176 pub fn with_name(mut self, name: String) -> Self {
178 self.name = Some(name);
179 self
180 }
181
182 pub fn matches(
184 &self,
185 facts: &TypedFacts,
186 bindings: &HashMap<Variable, FactValue>,
187 ) -> Option<HashMap<Variable, FactValue>> {
188 let mut new_bindings = bindings.clone();
189
190 for constraint in &self.constraints {
191 let additional_bindings = constraint.evaluate(facts, &new_bindings)?;
192 new_bindings.extend(additional_bindings);
193 }
194
195 Some(new_bindings)
196 }
197
198 pub fn match_in_working_memory(
200 &self,
201 wm: &WorkingMemory,
202 bindings: &HashMap<Variable, FactValue>,
203 ) -> Vec<(FactHandle, HashMap<Variable, FactValue>)> {
204 let mut results = Vec::new();
205
206 for fact in wm.get_by_type(&self.fact_type) {
207 if let Some(new_bindings) = self.matches(&fact.data, bindings) {
208 results.push((fact.handle, new_bindings));
209 }
210 }
211
212 results
213 }
214}
215
216#[derive(Debug, Clone)]
218pub struct MultiPattern {
219 pub patterns: Vec<Pattern>,
221 pub name: String,
223}
224
225impl MultiPattern {
226 pub fn new(name: String) -> Self {
228 Self {
229 patterns: Vec::new(),
230 name,
231 }
232 }
233
234 pub fn with_pattern(mut self, pattern: Pattern) -> Self {
236 self.patterns.push(pattern);
237 self
238 }
239
240 pub fn match_all(
242 &self,
243 wm: &WorkingMemory,
244 ) -> Vec<(Vec<FactHandle>, HashMap<Variable, FactValue>)> {
245 if self.patterns.is_empty() {
246 return Vec::new();
247 }
248
249 let mut results = Vec::new();
251 let first_pattern = &self.patterns[0];
252 let empty_bindings = HashMap::new();
253
254 for (handle, bindings) in first_pattern.match_in_working_memory(wm, &empty_bindings) {
255 results.push((vec![handle], bindings));
256 }
257
258 for pattern in &self.patterns[1..] {
260 let mut new_results = Vec::new();
261
262 for (handles, bindings) in results {
263 for (handle, new_bindings) in pattern.match_in_working_memory(wm, &bindings) {
264 let mut combined_handles = handles.clone();
265 combined_handles.push(handle);
266 new_results.push((combined_handles, new_bindings));
267 }
268 }
269
270 results = new_results;
271
272 if results.is_empty() {
273 break; }
275 }
276
277 results
278 }
279}
280
281pub struct PatternBuilder {
283 pattern: Pattern,
284}
285
286impl PatternBuilder {
287 pub fn for_type(fact_type: impl Into<String>) -> Self {
289 Self {
290 pattern: Pattern::new(fact_type.into()),
291 }
292 }
293
294 pub fn where_field(
296 mut self,
297 field: impl Into<String>,
298 operator: impl Into<String>,
299 value: FactValue,
300 ) -> Self {
301 self.pattern.constraints.push(PatternConstraint::Simple {
302 field: field.into(),
303 operator: operator.into(),
304 value,
305 });
306 self
307 }
308
309 pub fn bind(mut self, field: impl Into<String>, variable: impl Into<String>) -> Self {
311 self.pattern.constraints.push(PatternConstraint::Binding {
312 field: field.into(),
313 variable: variable.into(),
314 });
315 self
316 }
317
318 pub fn where_var(
320 mut self,
321 field: impl Into<String>,
322 operator: impl Into<String>,
323 variable: impl Into<String>,
324 ) -> Self {
325 self.pattern.constraints.push(PatternConstraint::Variable {
326 field: field.into(),
327 operator: operator.into(),
328 variable: variable.into(),
329 });
330 self
331 }
332
333 pub fn named(mut self, name: impl Into<String>) -> Self {
335 self.pattern.name = Some(name.into());
336 self
337 }
338
339 pub fn build(self) -> Pattern {
341 self.pattern
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 #[test]
350 fn test_simple_pattern() {
351 let pattern = PatternBuilder::for_type("Person")
352 .where_field("age", ">", FactValue::Integer(18))
353 .where_field("status", "==", FactValue::String("active".to_string()))
354 .build();
355
356 let mut facts = TypedFacts::new();
357 facts.set("age", 25i64);
358 facts.set("status", "active");
359
360 let bindings = HashMap::new();
361 let result = pattern.matches(&facts, &bindings);
362 assert!(result.is_some());
363 }
364
365 #[test]
366 fn test_variable_binding() {
367 let pattern = PatternBuilder::for_type("Person")
368 .bind("name", "$personName")
369 .bind("age", "$personAge")
370 .build();
371
372 let mut facts = TypedFacts::new();
373 facts.set("name", "John");
374 facts.set("age", 25i64);
375
376 let bindings = HashMap::new();
377 let result = pattern.matches(&facts, &bindings).unwrap();
378
379 assert_eq!(result.get("$personName").unwrap().as_string(), "John");
380 assert_eq!(result.get("$personAge").unwrap().as_integer(), Some(25));
381 }
382
383 #[test]
384 fn test_variable_constraint() {
385 let mut bindings = HashMap::new();
387 bindings.insert("$minAge".to_string(), FactValue::Integer(18));
388
389 let pattern = PatternBuilder::for_type("Person")
391 .where_var("age", ">=", "$minAge")
392 .build();
393
394 let mut facts = TypedFacts::new();
395 facts.set("age", 25i64);
396
397 let result = pattern.matches(&facts, &bindings);
398 assert!(result.is_some());
399 }
400
401 #[test]
402 fn test_multi_pattern_join() {
403 let mut wm = WorkingMemory::new();
404
405 let mut person = TypedFacts::new();
407 person.set("name", "John");
408 person.set("age", 25i64);
409 wm.insert("Person".to_string(), person);
410
411 let mut order = TypedFacts::new();
413 order.set("customer", "John");
414 order.set("amount", 1000.0);
415 wm.insert("Order".to_string(), order);
416
417 let person_pattern = PatternBuilder::for_type("Person")
419 .bind("name", "$name")
420 .build();
421
422 let order_pattern = PatternBuilder::for_type("Order")
423 .where_var("customer", "==", "$name")
424 .build();
425
426 let multi = MultiPattern::new("PersonWithOrder".to_string())
427 .with_pattern(person_pattern)
428 .with_pattern(order_pattern);
429
430 let matches = multi.match_all(&wm);
431 assert_eq!(matches.len(), 1);
432
433 let (handles, bindings) = &matches[0];
434 assert_eq!(handles.len(), 2);
435 assert_eq!(bindings.get("$name").unwrap().as_string(), "John");
436 }
437}