rust_rule_engine/backward/
query.rs1use super::goal::Goal;
4use crate::types::Value;
5use std::collections::HashMap;
6
7#[derive(Debug, Clone)]
9pub struct QueryResult {
10 pub provable: bool,
12
13 pub bindings: HashMap<String, Value>,
15
16 pub proof_trace: ProofTrace,
18
19 pub missing_facts: Vec<String>,
21
22 pub stats: QueryStats,
24}
25
26#[derive(Debug, Clone)]
28pub struct ProofTrace {
29 pub goal: String,
31
32 pub steps: Vec<ProofStep>,
34}
35
36#[derive(Debug, Clone)]
38pub struct ProofStep {
39 pub rule_name: String,
41
42 pub goal: String,
44
45 pub sub_steps: Vec<ProofStep>,
47
48 pub depth: usize,
50}
51
52#[derive(Debug, Clone, Default)]
54pub struct QueryStats {
55 pub goals_explored: usize,
57
58 pub rules_evaluated: usize,
60
61 pub max_depth: usize,
63
64 pub duration_ms: Option<u64>,
66}
67
68impl QueryResult {
69 pub fn success(bindings: HashMap<String, Value>, proof: ProofTrace, stats: QueryStats) -> Self {
71 Self {
72 provable: true,
73 bindings,
74 proof_trace: proof,
75 missing_facts: Vec::new(),
76 stats,
77 }
78 }
79
80 pub fn failure(missing: Vec<String>, stats: QueryStats) -> Self {
82 Self {
83 provable: false,
84 bindings: HashMap::new(),
85 proof_trace: ProofTrace::empty(),
86 missing_facts: missing,
87 stats,
88 }
89 }
90}
91
92impl ProofTrace {
93 pub fn empty() -> Self {
95 Self {
96 goal: String::new(),
97 steps: Vec::new(),
98 }
99 }
100
101 pub fn new(goal: String) -> Self {
103 Self {
104 goal,
105 steps: Vec::new(),
106 }
107 }
108
109 pub fn add_step(&mut self, step: ProofStep) {
111 self.steps.push(step);
112 }
113
114 pub fn from_goal(goal: &Goal) -> Self {
116 let mut trace = Self::new(goal.pattern.clone());
117
118 for (i, rule_name) in goal.candidate_rules.iter().enumerate() {
119 let step = ProofStep {
120 rule_name: rule_name.clone(),
121 goal: goal.pattern.clone(),
122 sub_steps: goal.sub_goals.iter()
123 .map(|sg| ProofStep::from_goal(sg, i + 1))
124 .collect(),
125 depth: goal.depth,
126 };
127 trace.add_step(step);
128 }
129
130 trace
131 }
132
133 pub fn print(&self) {
135 println!("Proof for goal: {}", self.goal);
136 for step in &self.steps {
137 step.print(0);
138 }
139 }
140}
141
142impl ProofStep {
143 fn from_goal(goal: &Goal, depth: usize) -> Self {
145 Self {
146 rule_name: goal.candidate_rules.first()
147 .cloned()
148 .unwrap_or_else(|| "unknown".to_string()),
149 goal: goal.pattern.clone(),
150 sub_steps: goal.sub_goals.iter()
151 .map(|sg| Self::from_goal(sg, depth + 1))
152 .collect(),
153 depth,
154 }
155 }
156
157 fn print(&self, indent: usize) {
159 let prefix = " ".repeat(indent);
160 println!("{}→ [{}] {}", prefix, self.rule_name, self.goal);
161 for sub in &self.sub_steps {
162 sub.print(indent + 1);
163 }
164 }
165}
166
167pub struct QueryParser;
169
170impl QueryParser {
171 pub fn parse(query: &str) -> Result<Goal, String> {
178 use super::expression::ExpressionParser;
179
180 if query.is_empty() {
182 return Err("Empty query".to_string());
183 }
184
185 match ExpressionParser::parse(query) {
187 Ok(expr) => {
188 Ok(Goal::with_expression(query.to_string(), expr))
189 }
190 Err(e) => {
191 Err(format!("Failed to parse query: {}", e))
192 }
193 }
194 }
195
196 pub fn validate(query: &str) -> Result<(), String> {
198 if query.is_empty() {
199 return Err("Query cannot be empty".to_string());
200 }
201
202 Self::parse(query).map(|_| ())
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_query_result_creation() {
213 let stats = QueryStats::default();
214
215 let success = QueryResult::success(
216 HashMap::new(),
217 ProofTrace::empty(),
218 stats.clone(),
219 );
220 assert!(success.provable);
221
222 let failure = QueryResult::failure(vec!["fact".to_string()], stats);
223 assert!(!failure.provable);
224 assert_eq!(failure.missing_facts.len(), 1);
225 }
226
227 #[test]
228 fn test_proof_trace() {
229 let mut trace = ProofTrace::new("User.IsVIP == true".to_string());
230 assert_eq!(trace.goal, "User.IsVIP == true");
231 assert!(trace.steps.is_empty());
232
233 let step = ProofStep {
234 rule_name: "VIPRule".to_string(),
235 goal: "User.IsVIP == true".to_string(),
236 sub_steps: Vec::new(),
237 depth: 0,
238 };
239
240 trace.add_step(step);
241 assert_eq!(trace.steps.len(), 1);
242 }
243
244 #[test]
245 fn test_proof_step() {
246 let step = ProofStep {
247 rule_name: "TestRule".to_string(),
248 goal: "test".to_string(),
249 sub_steps: Vec::new(),
250 depth: 0,
251 };
252
253 assert_eq!(step.rule_name, "TestRule");
254 assert_eq!(step.depth, 0);
255 }
256
257 #[test]
258 fn test_query_parser() {
259 let result = QueryParser::parse("User.IsVIP == true");
260 assert!(result.is_ok());
261
262 let empty = QueryParser::parse("");
263 assert!(empty.is_err());
264 }
265
266 #[test]
267 fn test_query_validation() {
268 assert!(QueryParser::validate("User.Age > 18").is_ok());
269 assert!(QueryParser::validate("User.IsVIP == true").is_ok());
270 assert!(QueryParser::validate("").is_err());
271 assert!(QueryParser::validate("(unclosed").is_err());
274 }
275
276 #[test]
277 fn test_query_stats() {
278 let stats = QueryStats {
279 goals_explored: 5,
280 rules_evaluated: 3,
281 max_depth: 2,
282 duration_ms: Some(100),
283 };
284
285 assert_eq!(stats.goals_explored, 5);
286 assert_eq!(stats.duration_ms, Some(100));
287 }
288}