rust_rule_engine/backward/
explanation.rs1use super::goal::Goal;
9use super::proof_tree::{ProofNode, ProofNodeType, ProofStats, ProofTree};
10use super::unification::Bindings;
11use std::collections::HashMap;
12
13#[derive(Debug, Clone)]
15pub struct ExplanationBuilder {
16 node_stack: Vec<ProofNode>,
18
19 goals_explored: usize,
21 rules_evaluated: usize,
22 facts_checked: usize,
23 max_depth: usize,
24
25 enabled: bool,
27}
28
29impl ExplanationBuilder {
30 pub fn new() -> Self {
32 ExplanationBuilder {
33 node_stack: Vec::new(),
34 goals_explored: 0,
35 rules_evaluated: 0,
36 facts_checked: 0,
37 max_depth: 0,
38 enabled: false,
39 }
40 }
41
42 pub fn enable(&mut self) {
44 self.enabled = true;
45 }
46
47 pub fn disable(&mut self) {
49 self.enabled = false;
50 }
51
52 pub fn is_enabled(&self) -> bool {
54 self.enabled
55 }
56
57 pub fn start_goal(&mut self, goal: &Goal) {
59 if !self.enabled {
60 return;
61 }
62
63 self.goals_explored += 1;
64 self.max_depth = self.max_depth.max(goal.depth);
65
66 let node = ProofNode::new(goal.pattern.clone(), goal.depth);
67 self.node_stack.push(node);
68 }
69
70 pub fn goal_proven_by_fact(&mut self, _goal: &Goal, bindings: &Bindings) {
72 if !self.enabled || self.node_stack.is_empty() {
73 return;
74 }
75
76 self.facts_checked += 1;
77
78 if let Some(node) = self.node_stack.last_mut() {
79 node.proven = true;
80 node.node_type = ProofNodeType::Fact;
81 node.set_bindings(bindings);
82 }
83 }
84
85 pub fn goal_proven_by_rule(&mut self, _goal: &Goal, rule_name: &str, bindings: &Bindings) {
87 if !self.enabled || self.node_stack.is_empty() {
88 return;
89 }
90
91 self.rules_evaluated += 1;
92
93 if let Some(node) = self.node_stack.last_mut() {
94 node.proven = true;
95 node.node_type = ProofNodeType::Rule;
96 node.rule_name = Some(rule_name.to_string());
97 node.set_bindings(bindings);
98 }
99 }
100
101 pub fn goal_negation(&mut self, _goal: &Goal, proven: bool) {
103 if !self.enabled || self.node_stack.is_empty() {
104 return;
105 }
106
107 if let Some(node) = self.node_stack.last_mut() {
108 node.proven = proven;
109 node.node_type = ProofNodeType::Negation;
110 }
111 }
112
113 pub fn goal_failed(&mut self) {
115 if !self.enabled || self.node_stack.is_empty() {
116 return;
117 }
118
119 if let Some(node) = self.node_stack.last_mut() {
120 node.proven = false;
121 node.node_type = ProofNodeType::Failed;
122 }
123 }
124
125 pub fn finish_goal(&mut self) {
127 if !self.enabled || self.node_stack.is_empty() {
128 return;
129 }
130
131 let finished_node = self.node_stack.pop().unwrap();
132
133 if let Some(parent) = self.node_stack.last_mut() {
135 parent.add_child(finished_node);
136 } else {
137 self.node_stack.push(finished_node);
139 }
140 }
141
142 pub fn build(self, query: String) -> Option<ProofTree> {
144 if !self.enabled || self.node_stack.is_empty() {
145 return None;
146 }
147
148 let root = self.node_stack.into_iter().next().unwrap();
149 let mut tree = ProofTree::new(root, query);
150
151 tree.set_stats(ProofStats {
152 goals_explored: self.goals_explored,
153 rules_evaluated: self.rules_evaluated,
154 facts_checked: self.facts_checked,
155 max_depth: self.max_depth,
156 total_nodes: tree.root.node_count(),
157 });
158
159 Some(tree)
160 }
161
162 pub fn stats(&self) -> ProofStats {
164 ProofStats {
165 goals_explored: self.goals_explored,
166 rules_evaluated: self.rules_evaluated,
167 facts_checked: self.facts_checked,
168 max_depth: self.max_depth,
169 total_nodes: self.node_stack.len(),
170 }
171 }
172
173 pub fn reset(&mut self) {
175 self.node_stack.clear();
176 self.goals_explored = 0;
177 self.rules_evaluated = 0;
178 self.facts_checked = 0;
179 self.max_depth = 0;
180 }
181}
182
183impl Default for ExplanationBuilder {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189#[derive(Debug, Clone)]
191pub struct ExplanationStep {
192 pub step_number: usize,
194
195 pub goal: String,
197
198 pub rule_name: Option<String>,
200
201 pub condition: String,
203
204 pub bindings: HashMap<String, String>,
206
207 pub result: StepResult,
209
210 pub depth: usize,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq)]
216pub enum StepResult {
217 Success,
219
220 Failed,
222
223 Skipped,
225}
226
227impl ExplanationStep {
228 pub fn new(
230 step_number: usize,
231 goal: String,
232 rule_name: Option<String>,
233 condition: String,
234 bindings: HashMap<String, String>,
235 result: StepResult,
236 depth: usize,
237 ) -> Self {
238 ExplanationStep {
239 step_number,
240 goal,
241 rule_name,
242 condition,
243 bindings,
244 result,
245 depth,
246 }
247 }
248
249 pub fn format(&self) -> String {
251 let mut s = String::new();
252
253 s.push_str(&format!("Step {}: {}\n", self.step_number, self.goal));
254
255 if let Some(rule) = &self.rule_name {
256 s.push_str(&format!(" Rule: {}\n", rule));
257 }
258
259 s.push_str(&format!(" Condition: {}\n", self.condition));
260
261 if !self.bindings.is_empty() {
262 s.push_str(&format!(" Bindings: {:?}\n", self.bindings));
263 }
264
265 s.push_str(&format!(" Result: {:?}\n", self.result));
266
267 s
268 }
269}
270
271#[derive(Debug, Clone)]
273pub struct Explanation {
274 pub query: String,
276
277 pub proof_tree: ProofTree,
279
280 pub steps: Vec<ExplanationStep>,
282
283 pub summary: String,
285}
286
287impl Explanation {
288 pub fn new(query: String, proof_tree: ProofTree) -> Self {
290 let steps = Self::tree_to_steps(&proof_tree.root);
291 let summary = Self::generate_summary(&proof_tree);
292
293 Explanation {
294 query,
295 proof_tree,
296 steps,
297 summary,
298 }
299 }
300
301 fn tree_to_steps(root: &ProofNode) -> Vec<ExplanationStep> {
303 let mut steps = Vec::new();
304 Self::collect_steps(root, &mut steps, 1);
305 steps
306 }
307
308 fn collect_steps(
310 node: &ProofNode,
311 steps: &mut Vec<ExplanationStep>,
312 mut step_num: usize,
313 ) -> usize {
314 let result = if node.proven {
315 StepResult::Success
316 } else {
317 StepResult::Failed
318 };
319
320 let condition = match &node.node_type {
321 ProofNodeType::Fact => format!("{} [FACT]", node.goal),
322 ProofNodeType::Rule => node.goal.clone(),
323 ProofNodeType::Negation => format!("NOT {}", node.goal),
324 ProofNodeType::Failed => format!("{} [FAILED]", node.goal),
325 };
326
327 let step = ExplanationStep::new(
328 step_num,
329 node.goal.clone(),
330 node.rule_name.clone(),
331 condition,
332 node.bindings.clone(),
333 result,
334 node.depth,
335 );
336
337 steps.push(step);
338 step_num += 1;
339
340 for child in &node.children {
342 step_num = Self::collect_steps(child, steps, step_num);
343 }
344
345 step_num
346 }
347
348 fn generate_summary(tree: &ProofTree) -> String {
350 if tree.success {
351 format!(
352 "Query '{}' was successfully proven using {} rules and {} facts.",
353 tree.query, tree.stats.rules_evaluated, tree.stats.facts_checked
354 )
355 } else {
356 format!(
357 "Query '{}' could not be proven. Explored {} goals, evaluated {} rules, and checked {} facts.",
358 tree.query,
359 tree.stats.goals_explored,
360 tree.stats.rules_evaluated,
361 tree.stats.facts_checked
362 )
363 }
364 }
365
366 pub fn print(&self) {
368 println!(
369 "================================================================================"
370 );
371 println!("EXPLANATION");
372 println!(
373 "================================================================================"
374 );
375 println!("\nQuery: {}", self.query);
376 println!(
377 "Result: {}",
378 if self.proof_tree.success {
379 "✓ Proven"
380 } else {
381 "✗ Unprovable"
382 }
383 );
384 println!("\n{}\n", self.summary);
385
386 println!("Step-by-Step Reasoning:");
387 println!("{}", "-".repeat(80));
388 for step in &self.steps {
389 print!("{}", step.format());
390 }
391
392 println!("\n{}", "=".repeat(80));
393 self.proof_tree.print_stats();
394 println!("{}", "=".repeat(80));
395 }
396
397 pub fn to_json(&self) -> Result<String, serde_json::Error> {
399 #[derive(serde::Serialize)]
400 struct ExplanationJson<'a> {
401 query: &'a str,
402 success: bool,
403 summary: &'a str,
404 proof_tree: &'a ProofTree,
405 stats: &'a ProofStats,
406 }
407
408 let json = ExplanationJson {
409 query: &self.query,
410 success: self.proof_tree.success,
411 summary: &self.summary,
412 proof_tree: &self.proof_tree,
413 stats: &self.proof_tree.stats,
414 };
415
416 serde_json::to_string_pretty(&json)
417 }
418
419 pub fn to_markdown(&self) -> String {
421 let mut md = String::new();
422
423 md.push_str("# Query Explanation\n\n");
424 md.push_str(&format!("**Query:** `{}`\n\n", self.query));
425 md.push_str(&format!(
426 "**Result:** {}\n\n",
427 if self.proof_tree.success {
428 "✓ Proven"
429 } else {
430 "✗ Unprovable"
431 }
432 ));
433 md.push_str(&format!("**Summary:** {}\n\n", self.summary));
434
435 md.push_str("## Proof Tree\n\n");
436 md.push_str(&self.proof_tree.to_markdown());
437
438 md
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445
446 #[test]
447 fn test_explanation_builder_creation() {
448 let builder = ExplanationBuilder::new();
449 assert!(!builder.is_enabled());
450 assert_eq!(builder.goals_explored, 0);
451 }
452
453 #[test]
454 fn test_enable_disable() {
455 let mut builder = ExplanationBuilder::new();
456 builder.enable();
457 assert!(builder.is_enabled());
458
459 builder.disable();
460 assert!(!builder.is_enabled());
461 }
462
463 #[test]
464 fn test_tracking_goal() {
465 let mut builder = ExplanationBuilder::new();
466 builder.enable();
467
468 let goal = Goal::new("test".to_string());
469 builder.start_goal(&goal);
470
471 assert_eq!(builder.goals_explored, 1);
472 assert_eq!(builder.node_stack.len(), 1);
473 }
474
475 #[test]
476 fn test_goal_proven_by_fact() {
477 let mut builder = ExplanationBuilder::new();
478 builder.enable();
479
480 let goal = Goal::new("test".to_string());
481 builder.start_goal(&goal);
482
483 let bindings = Bindings::new();
484 builder.goal_proven_by_fact(&goal, &bindings);
485
486 assert_eq!(builder.facts_checked, 1);
487 assert!(builder.node_stack[0].proven);
488 assert_eq!(builder.node_stack[0].node_type, ProofNodeType::Fact);
489 }
490
491 #[test]
492 fn test_build_proof_tree() {
493 let mut builder = ExplanationBuilder::new();
494 builder.enable();
495
496 let goal = Goal::new("test".to_string());
497 builder.start_goal(&goal);
498
499 let bindings = Bindings::new();
500 builder.goal_proven_by_fact(&goal, &bindings);
501
502 let tree = builder.build("test query".to_string());
503 assert!(tree.is_some());
504
505 let tree = tree.unwrap();
506 assert_eq!(tree.query, "test query");
507 assert!(tree.success);
508 }
509
510 #[test]
511 fn test_explanation_step() {
512 let step = ExplanationStep::new(
513 1,
514 "test_goal".to_string(),
515 Some("test_rule".to_string()),
516 "condition".to_string(),
517 HashMap::new(),
518 StepResult::Success,
519 0,
520 );
521
522 assert_eq!(step.step_number, 1);
523 assert_eq!(step.result, StepResult::Success);
524 }
525}