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
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum GoalStatus {
36 Pending,
38
39 InProgress,
41
42 Proven,
44
45 Unprovable,
47}
48
49impl Goal {
50 pub fn new(pattern: String) -> Self {
52 Self {
53 pattern,
54 expression: None,
55 status: GoalStatus::Pending,
56 sub_goals: Vec::new(),
57 candidate_rules: Vec::new(),
58 bindings: Bindings::new(),
59 depth: 0,
60 }
61 }
62
63 pub fn with_expression(pattern: String, expression: Expression) -> Self {
65 Self {
66 pattern,
67 expression: Some(expression),
68 status: GoalStatus::Pending,
69 sub_goals: Vec::new(),
70 candidate_rules: Vec::new(),
71 bindings: Bindings::new(),
72 depth: 0,
73 }
74 }
75
76 pub fn is_proven(&self) -> bool {
78 self.status == GoalStatus::Proven
79 }
80
81 pub fn is_unprovable(&self) -> bool {
83 self.status == GoalStatus::Unprovable
84 }
85
86 pub fn all_subgoals_proven(&self) -> bool {
88 self.sub_goals.iter().all(|g| g.is_proven())
89 }
90
91 pub fn add_subgoal(&mut self, goal: Goal) {
93 self.sub_goals.push(goal);
94 }
95
96 pub fn add_candidate_rule(&mut self, rule_name: String) {
98 if !self.candidate_rules.contains(&rule_name) {
99 self.candidate_rules.push(rule_name);
100 }
101 }
102}
103
104#[derive(Debug)]
106pub struct GoalManager {
107 goals: Vec<Goal>,
109
110 max_depth: usize,
112
113 proven_cache: HashMap<String, bool>,
115}
116
117impl GoalManager {
118 pub fn new(max_depth: usize) -> Self {
120 Self {
121 goals: Vec::new(),
122 max_depth,
123 proven_cache: HashMap::new(),
124 }
125 }
126
127 pub fn add_goal(&mut self, goal: Goal) {
129 self.goals.push(goal);
130 }
131
132 pub fn next_pending(&mut self) -> Option<&mut Goal> {
134 self.goals.iter_mut()
135 .find(|g| g.status == GoalStatus::Pending)
136 }
137
138 pub fn is_cached(&self, pattern: &str) -> Option<bool> {
140 self.proven_cache.get(pattern).copied()
141 }
142
143 pub fn cache_result(&mut self, pattern: String, proven: bool) {
145 self.proven_cache.insert(pattern, proven);
146 }
147
148 pub fn is_too_deep(&self, depth: usize) -> bool {
150 depth > self.max_depth
151 }
152
153 pub fn goals(&self) -> &[Goal] {
155 &self.goals
156 }
157
158 pub fn clear(&mut self) {
160 self.goals.clear();
161 self.proven_cache.clear();
162 }
163}
164
165impl Default for GoalManager {
166 fn default() -> Self {
167 Self::new(10) }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_goal_creation() {
177 let goal = Goal::new("User.IsVIP == true".to_string());
178 assert_eq!(goal.status, GoalStatus::Pending);
179 assert_eq!(goal.depth, 0);
180 assert!(goal.sub_goals.is_empty());
181 }
182
183 #[test]
184 fn test_goal_status_checks() {
185 let mut goal = Goal::new("test".to_string());
186 assert!(!goal.is_proven());
187
188 goal.status = GoalStatus::Proven;
189 assert!(goal.is_proven());
190
191 goal.status = GoalStatus::Unprovable;
192 assert!(goal.is_unprovable());
193 }
194
195 #[test]
196 fn test_subgoal_management() {
197 let mut parent = Goal::new("parent".to_string());
198 let mut child1 = Goal::new("child1".to_string());
199 let mut child2 = Goal::new("child2".to_string());
200
201 child1.status = GoalStatus::Proven;
202 child2.status = GoalStatus::Proven;
203
204 parent.add_subgoal(child1);
205 parent.add_subgoal(child2);
206
207 assert_eq!(parent.sub_goals.len(), 2);
208 assert!(parent.all_subgoals_proven());
209 }
210
211 #[test]
212 fn test_goal_manager() {
213 let mut manager = GoalManager::new(5);
214
215 let goal1 = Goal::new("goal1".to_string());
216 let goal2 = Goal::new("goal2".to_string());
217
218 manager.add_goal(goal1);
219 manager.add_goal(goal2);
220
221 assert_eq!(manager.goals().len(), 2);
222
223 assert!(manager.is_cached("goal1").is_none());
225 manager.cache_result("goal1".to_string(), true);
226 assert_eq!(manager.is_cached("goal1"), Some(true));
227
228 assert!(!manager.is_too_deep(3));
230 assert!(manager.is_too_deep(10));
231 }
232
233 #[test]
234 fn test_candidate_rules() {
235 let mut goal = Goal::new("test".to_string());
236
237 goal.add_candidate_rule("Rule1".to_string());
238 goal.add_candidate_rule("Rule2".to_string());
239 goal.add_candidate_rule("Rule1".to_string()); assert_eq!(goal.candidate_rules.len(), 2);
242 }
243
244 #[test]
245 fn test_goal_with_expression() {
246 use super::super::expression::Expression;
247 use crate::types::{Operator, Value};
248
249 let expr = Expression::Comparison {
250 left: Box::new(Expression::Field("User.IsVIP".to_string())),
251 operator: Operator::Equal,
252 right: Box::new(Expression::Literal(Value::Boolean(true))),
253 };
254
255 let goal = Goal::with_expression("User.IsVIP == true".to_string(), expr);
256 assert!(goal.expression.is_some());
257 assert_eq!(goal.pattern, "User.IsVIP == true");
258 assert_eq!(goal.status, GoalStatus::Pending);
259 }
260
261 #[test]
262 fn test_subgoals_not_all_proven() {
263 let mut parent = Goal::new("parent".to_string());
264 let mut child1 = Goal::new("child1".to_string());
265 let mut child2 = Goal::new("child2".to_string());
266
267 child1.status = GoalStatus::Proven;
268 child2.status = GoalStatus::Pending; parent.add_subgoal(child1);
271 parent.add_subgoal(child2);
272
273 assert_eq!(parent.sub_goals.len(), 2);
274 assert!(!parent.all_subgoals_proven());
275 }
276
277 #[test]
278 fn test_goal_manager_next_pending() {
279 let mut manager = GoalManager::new(5);
280
281 let mut goal1 = Goal::new("goal1".to_string());
282 goal1.status = GoalStatus::Proven; let goal2 = Goal::new("goal2".to_string()); let mut goal3 = Goal::new("goal3".to_string());
287 goal3.status = GoalStatus::InProgress;
288
289 manager.add_goal(goal1);
290 manager.add_goal(goal2);
291 manager.add_goal(goal3);
292
293 let next = manager.next_pending();
295 assert!(next.is_some());
296 assert_eq!(next.unwrap().pattern, "goal2");
297 }
298
299 #[test]
300 fn test_goal_manager_clear() {
301 let mut manager = GoalManager::new(5);
302
303 manager.add_goal(Goal::new("goal1".to_string()));
304 manager.add_goal(Goal::new("goal2".to_string()));
305 manager.cache_result("goal1".to_string(), true);
306
307 assert_eq!(manager.goals().len(), 2);
308 assert!(manager.is_cached("goal1").is_some());
309
310 manager.clear();
311
312 assert_eq!(manager.goals().len(), 0);
313 assert!(manager.is_cached("goal1").is_none());
314 }
315
316 #[test]
317 fn test_goal_bindings() {
318 let mut goal = Goal::new("User.?X == true".to_string());
319
320 assert!(goal.bindings.get("X").is_none());
322
323 goal.bindings.bind("X".to_string(), Value::String("IsVIP".to_string())).ok();
325
326 assert!(goal.bindings.get("X").is_some());
328 assert_eq!(goal.bindings.get("X"), Some(&Value::String("IsVIP".to_string())));
329 }
330
331 #[test]
332 fn test_goal_depth() {
333 let mut goal = Goal::new("test".to_string());
334 assert_eq!(goal.depth, 0);
335
336 goal.depth = 5;
337 assert_eq!(goal.depth, 5);
338 }
339
340 #[test]
341 fn test_goal_manager_default() {
342 let manager = GoalManager::default();
343 assert_eq!(manager.max_depth, 10);
344 assert_eq!(manager.goals().len(), 0);
345 }
346}