rust_rule_engine/backward/
goal.rs1use crate::types::Value;
4use std::collections::HashMap;
5use super::expression::Expression;
6use super::unification::Bindings;
7
8#[derive(Debug, Clone)]
10pub struct Goal {
11 pub pattern: String,
13
14 pub expression: Option<Expression>,
16
17 pub status: GoalStatus,
19
20 pub sub_goals: Vec<Goal>,
22
23 pub candidate_rules: Vec<String>,
25
26 pub bindings: Bindings,
28
29 pub depth: usize,
31
32 pub is_negated: bool,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum GoalStatus {
40 Pending,
42
43 InProgress,
45
46 Proven,
48
49 Unprovable,
51}
52
53impl Goal {
54 pub fn new(pattern: String) -> Self {
56 Self {
57 pattern,
58 expression: None,
59 status: GoalStatus::Pending,
60 sub_goals: Vec::new(),
61 candidate_rules: Vec::new(),
62 bindings: Bindings::new(),
63 depth: 0,
64 is_negated: false,
65 }
66 }
67
68 pub fn with_expression(pattern: String, expression: Expression) -> Self {
70 Self {
71 pattern,
72 expression: Some(expression),
73 status: GoalStatus::Pending,
74 sub_goals: Vec::new(),
75 candidate_rules: Vec::new(),
76 bindings: Bindings::new(),
77 depth: 0,
78 is_negated: false,
79 }
80 }
81
82 pub fn negated(pattern: String) -> Self {
84 Self {
85 pattern,
86 expression: None,
87 status: GoalStatus::Pending,
88 sub_goals: Vec::new(),
89 candidate_rules: Vec::new(),
90 bindings: Bindings::new(),
91 depth: 0,
92 is_negated: true,
93 }
94 }
95
96 pub fn negated_with_expression(pattern: String, expression: Expression) -> Self {
98 Self {
99 pattern,
100 expression: Some(expression),
101 status: GoalStatus::Pending,
102 sub_goals: Vec::new(),
103 candidate_rules: Vec::new(),
104 bindings: Bindings::new(),
105 depth: 0,
106 is_negated: true,
107 }
108 }
109
110 pub fn is_proven(&self) -> bool {
112 self.status == GoalStatus::Proven
113 }
114
115 pub fn is_unprovable(&self) -> bool {
117 self.status == GoalStatus::Unprovable
118 }
119
120 pub fn all_subgoals_proven(&self) -> bool {
122 self.sub_goals.iter().all(|g| g.is_proven())
123 }
124
125 pub fn add_subgoal(&mut self, goal: Goal) {
127 self.sub_goals.push(goal);
128 }
129
130 pub fn add_candidate_rule(&mut self, rule_name: String) {
132 if !self.candidate_rules.contains(&rule_name) {
133 self.candidate_rules.push(rule_name);
134 }
135 }
136}
137
138#[derive(Debug)]
140pub struct GoalManager {
141 goals: Vec<Goal>,
143
144 max_depth: usize,
146
147 proven_cache: HashMap<String, bool>,
149}
150
151impl GoalManager {
152 pub fn new(max_depth: usize) -> Self {
154 Self {
155 goals: Vec::new(),
156 max_depth,
157 proven_cache: HashMap::new(),
158 }
159 }
160
161 pub fn add_goal(&mut self, goal: Goal) {
163 self.goals.push(goal);
164 }
165
166 pub fn next_pending(&mut self) -> Option<&mut Goal> {
168 self.goals.iter_mut()
169 .find(|g| g.status == GoalStatus::Pending)
170 }
171
172 pub fn is_cached(&self, pattern: &str) -> Option<bool> {
174 self.proven_cache.get(pattern).copied()
175 }
176
177 pub fn cache_result(&mut self, pattern: String, proven: bool) {
179 self.proven_cache.insert(pattern, proven);
180 }
181
182 pub fn is_too_deep(&self, depth: usize) -> bool {
184 depth > self.max_depth
185 }
186
187 pub fn goals(&self) -> &[Goal] {
189 &self.goals
190 }
191
192 pub fn clear(&mut self) {
194 self.goals.clear();
195 self.proven_cache.clear();
196 }
197}
198
199impl Default for GoalManager {
200 fn default() -> Self {
201 Self::new(10) }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn test_goal_creation() {
211 let goal = Goal::new("User.IsVIP == true".to_string());
212 assert_eq!(goal.status, GoalStatus::Pending);
213 assert_eq!(goal.depth, 0);
214 assert!(goal.sub_goals.is_empty());
215 }
216
217 #[test]
218 fn test_goal_status_checks() {
219 let mut goal = Goal::new("test".to_string());
220 assert!(!goal.is_proven());
221
222 goal.status = GoalStatus::Proven;
223 assert!(goal.is_proven());
224
225 goal.status = GoalStatus::Unprovable;
226 assert!(goal.is_unprovable());
227 }
228
229 #[test]
230 fn test_subgoal_management() {
231 let mut parent = Goal::new("parent".to_string());
232 let mut child1 = Goal::new("child1".to_string());
233 let mut child2 = Goal::new("child2".to_string());
234
235 child1.status = GoalStatus::Proven;
236 child2.status = GoalStatus::Proven;
237
238 parent.add_subgoal(child1);
239 parent.add_subgoal(child2);
240
241 assert_eq!(parent.sub_goals.len(), 2);
242 assert!(parent.all_subgoals_proven());
243 }
244
245 #[test]
246 fn test_goal_manager() {
247 let mut manager = GoalManager::new(5);
248
249 let goal1 = Goal::new("goal1".to_string());
250 let goal2 = Goal::new("goal2".to_string());
251
252 manager.add_goal(goal1);
253 manager.add_goal(goal2);
254
255 assert_eq!(manager.goals().len(), 2);
256
257 assert!(manager.is_cached("goal1").is_none());
259 manager.cache_result("goal1".to_string(), true);
260 assert_eq!(manager.is_cached("goal1"), Some(true));
261
262 assert!(!manager.is_too_deep(3));
264 assert!(manager.is_too_deep(10));
265 }
266
267 #[test]
268 fn test_candidate_rules() {
269 let mut goal = Goal::new("test".to_string());
270
271 goal.add_candidate_rule("Rule1".to_string());
272 goal.add_candidate_rule("Rule2".to_string());
273 goal.add_candidate_rule("Rule1".to_string()); assert_eq!(goal.candidate_rules.len(), 2);
276 }
277
278 #[test]
279 fn test_goal_with_expression() {
280 use super::super::expression::Expression;
281 use crate::types::{Operator, Value};
282
283 let expr = Expression::Comparison {
284 left: Box::new(Expression::Field("User.IsVIP".to_string())),
285 operator: Operator::Equal,
286 right: Box::new(Expression::Literal(Value::Boolean(true))),
287 };
288
289 let goal = Goal::with_expression("User.IsVIP == true".to_string(), expr);
290 assert!(goal.expression.is_some());
291 assert_eq!(goal.pattern, "User.IsVIP == true");
292 assert_eq!(goal.status, GoalStatus::Pending);
293 }
294
295 #[test]
296 fn test_subgoals_not_all_proven() {
297 let mut parent = Goal::new("parent".to_string());
298 let mut child1 = Goal::new("child1".to_string());
299 let mut child2 = Goal::new("child2".to_string());
300
301 child1.status = GoalStatus::Proven;
302 child2.status = GoalStatus::Pending; parent.add_subgoal(child1);
305 parent.add_subgoal(child2);
306
307 assert_eq!(parent.sub_goals.len(), 2);
308 assert!(!parent.all_subgoals_proven());
309 }
310
311 #[test]
312 fn test_goal_manager_next_pending() {
313 let mut manager = GoalManager::new(5);
314
315 let mut goal1 = Goal::new("goal1".to_string());
316 goal1.status = GoalStatus::Proven; let goal2 = Goal::new("goal2".to_string()); let mut goal3 = Goal::new("goal3".to_string());
321 goal3.status = GoalStatus::InProgress;
322
323 manager.add_goal(goal1);
324 manager.add_goal(goal2);
325 manager.add_goal(goal3);
326
327 let next = manager.next_pending();
329 assert!(next.is_some());
330 assert_eq!(next.unwrap().pattern, "goal2");
331 }
332
333 #[test]
334 fn test_goal_manager_clear() {
335 let mut manager = GoalManager::new(5);
336
337 manager.add_goal(Goal::new("goal1".to_string()));
338 manager.add_goal(Goal::new("goal2".to_string()));
339 manager.cache_result("goal1".to_string(), true);
340
341 assert_eq!(manager.goals().len(), 2);
342 assert!(manager.is_cached("goal1").is_some());
343
344 manager.clear();
345
346 assert_eq!(manager.goals().len(), 0);
347 assert!(manager.is_cached("goal1").is_none());
348 }
349
350 #[test]
351 fn test_goal_bindings() {
352 let mut goal = Goal::new("User.?X == true".to_string());
353
354 assert!(goal.bindings.get("X").is_none());
356
357 goal.bindings.bind("X".to_string(), Value::String("IsVIP".to_string())).ok();
359
360 assert!(goal.bindings.get("X").is_some());
362 assert_eq!(goal.bindings.get("X"), Some(&Value::String("IsVIP".to_string())));
363 }
364
365 #[test]
366 fn test_goal_depth() {
367 let mut goal = Goal::new("test".to_string());
368 assert_eq!(goal.depth, 0);
369
370 goal.depth = 5;
371 assert_eq!(goal.depth, 5);
372 }
373
374 #[test]
375 fn test_goal_manager_default() {
376 let manager = GoalManager::default();
377 assert_eq!(manager.max_depth, 10);
378 assert_eq!(manager.goals().len(), 0);
379 }
380
381 #[test]
382 fn test_negated_goal() {
383 let goal = Goal::negated("User.IsBanned == true".to_string());
384 assert_eq!(goal.pattern, "User.IsBanned == true");
385 assert!(goal.is_negated);
386 assert_eq!(goal.status, GoalStatus::Pending);
387 }
388
389 #[test]
390 fn test_negated_goal_with_expression() {
391 use super::super::expression::Expression;
392 use crate::types::{Operator, Value};
393
394 let expr = Expression::Comparison {
395 left: Box::new(Expression::Field("User.IsBanned".to_string())),
396 operator: Operator::Equal,
397 right: Box::new(Expression::Literal(Value::Boolean(true))),
398 };
399
400 let goal = Goal::negated_with_expression("User.IsBanned == true".to_string(), expr);
401 assert!(goal.expression.is_some());
402 assert!(goal.is_negated);
403 assert_eq!(goal.pattern, "User.IsBanned == true");
404 }
405
406 #[test]
407 fn test_normal_goal_not_negated() {
408 let goal = Goal::new("test".to_string());
409 assert!(!goal.is_negated);
410
411 let expr = Expression::Field("test".to_string());
412 let goal_with_expr = Goal::with_expression("test".to_string(), expr);
413 assert!(!goal_with_expr.is_negated);
414 }
415}