rust_rule_engine/backward/
goal.rs1use crate::types::Value;
4use std::collections::HashMap;
5use super::expression::Expression;
6
7#[derive(Debug, Clone)]
9pub struct Goal {
10 pub pattern: String,
12
13 pub expression: Option<Expression>,
15
16 pub status: GoalStatus,
18
19 pub sub_goals: Vec<Goal>,
21
22 pub candidate_rules: Vec<String>,
24
25 pub bindings: HashMap<String, Value>,
27
28 pub depth: usize,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum GoalStatus {
35 Pending,
37
38 InProgress,
40
41 Proven,
43
44 Unprovable,
46}
47
48impl Goal {
49 pub fn new(pattern: String) -> Self {
51 Self {
52 pattern,
53 expression: None,
54 status: GoalStatus::Pending,
55 sub_goals: Vec::new(),
56 candidate_rules: Vec::new(),
57 bindings: HashMap::new(),
58 depth: 0,
59 }
60 }
61
62 pub fn with_expression(pattern: String, expression: Expression) -> Self {
64 Self {
65 pattern,
66 expression: Some(expression),
67 status: GoalStatus::Pending,
68 sub_goals: Vec::new(),
69 candidate_rules: Vec::new(),
70 bindings: HashMap::new(),
71 depth: 0,
72 }
73 }
74
75 pub fn is_proven(&self) -> bool {
77 self.status == GoalStatus::Proven
78 }
79
80 pub fn is_unprovable(&self) -> bool {
82 self.status == GoalStatus::Unprovable
83 }
84
85 pub fn all_subgoals_proven(&self) -> bool {
87 self.sub_goals.iter().all(|g| g.is_proven())
88 }
89
90 pub fn add_subgoal(&mut self, goal: Goal) {
92 self.sub_goals.push(goal);
93 }
94
95 pub fn add_candidate_rule(&mut self, rule_name: String) {
97 if !self.candidate_rules.contains(&rule_name) {
98 self.candidate_rules.push(rule_name);
99 }
100 }
101}
102
103#[derive(Debug)]
105pub struct GoalManager {
106 goals: Vec<Goal>,
108
109 max_depth: usize,
111
112 proven_cache: HashMap<String, bool>,
114}
115
116impl GoalManager {
117 pub fn new(max_depth: usize) -> Self {
119 Self {
120 goals: Vec::new(),
121 max_depth,
122 proven_cache: HashMap::new(),
123 }
124 }
125
126 pub fn add_goal(&mut self, goal: Goal) {
128 self.goals.push(goal);
129 }
130
131 pub fn next_pending(&mut self) -> Option<&mut Goal> {
133 self.goals.iter_mut()
134 .find(|g| g.status == GoalStatus::Pending)
135 }
136
137 pub fn is_cached(&self, pattern: &str) -> Option<bool> {
139 self.proven_cache.get(pattern).copied()
140 }
141
142 pub fn cache_result(&mut self, pattern: String, proven: bool) {
144 self.proven_cache.insert(pattern, proven);
145 }
146
147 pub fn is_too_deep(&self, depth: usize) -> bool {
149 depth > self.max_depth
150 }
151
152 pub fn goals(&self) -> &[Goal] {
154 &self.goals
155 }
156
157 pub fn clear(&mut self) {
159 self.goals.clear();
160 self.proven_cache.clear();
161 }
162}
163
164impl Default for GoalManager {
165 fn default() -> Self {
166 Self::new(10) }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_goal_creation() {
176 let goal = Goal::new("User.IsVIP == true".to_string());
177 assert_eq!(goal.status, GoalStatus::Pending);
178 assert_eq!(goal.depth, 0);
179 assert!(goal.sub_goals.is_empty());
180 }
181
182 #[test]
183 fn test_goal_status_checks() {
184 let mut goal = Goal::new("test".to_string());
185 assert!(!goal.is_proven());
186
187 goal.status = GoalStatus::Proven;
188 assert!(goal.is_proven());
189
190 goal.status = GoalStatus::Unprovable;
191 assert!(goal.is_unprovable());
192 }
193
194 #[test]
195 fn test_subgoal_management() {
196 let mut parent = Goal::new("parent".to_string());
197 let mut child1 = Goal::new("child1".to_string());
198 let mut child2 = Goal::new("child2".to_string());
199
200 child1.status = GoalStatus::Proven;
201 child2.status = GoalStatus::Proven;
202
203 parent.add_subgoal(child1);
204 parent.add_subgoal(child2);
205
206 assert_eq!(parent.sub_goals.len(), 2);
207 assert!(parent.all_subgoals_proven());
208 }
209
210 #[test]
211 fn test_goal_manager() {
212 let mut manager = GoalManager::new(5);
213
214 let goal1 = Goal::new("goal1".to_string());
215 let goal2 = Goal::new("goal2".to_string());
216
217 manager.add_goal(goal1);
218 manager.add_goal(goal2);
219
220 assert_eq!(manager.goals().len(), 2);
221
222 assert!(manager.is_cached("goal1").is_none());
224 manager.cache_result("goal1".to_string(), true);
225 assert_eq!(manager.is_cached("goal1"), Some(true));
226
227 assert!(!manager.is_too_deep(3));
229 assert!(manager.is_too_deep(10));
230 }
231
232 #[test]
233 fn test_candidate_rules() {
234 let mut goal = Goal::new("test".to_string());
235
236 goal.add_candidate_rule("Rule1".to_string());
237 goal.add_candidate_rule("Rule2".to_string());
238 goal.add_candidate_rule("Rule1".to_string()); assert_eq!(goal.candidate_rules.len(), 2);
241 }
242}