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
289 #[test]
290 fn test_query_stats_default() {
291 let stats = QueryStats::default();
292 assert_eq!(stats.goals_explored, 0);
293 assert_eq!(stats.rules_evaluated, 0);
294 assert_eq!(stats.max_depth, 0);
295 assert_eq!(stats.duration_ms, None);
296 }
297
298 #[test]
299 fn test_query_result_with_bindings() {
300 let mut bindings = HashMap::new();
301 bindings.insert("X".to_string(), Value::String("VIP".to_string()));
302 bindings.insert("Y".to_string(), Value::Number(1000.0));
303
304 let stats = QueryStats::default();
305 let result = QueryResult::success(bindings, ProofTrace::empty(), stats);
306
307 assert!(result.provable);
308 assert_eq!(result.bindings.len(), 2);
309 assert_eq!(result.bindings.get("X"), Some(&Value::String("VIP".to_string())));
310 assert_eq!(result.bindings.get("Y"), Some(&Value::Number(1000.0)));
311 }
312
313 #[test]
314 fn test_query_result_failure_with_missing_facts() {
315 let missing = vec![
316 "User.IsVIP".to_string(),
317 "Order.Total".to_string(),
318 ];
319
320 let stats = QueryStats::default();
321 let result = QueryResult::failure(missing, stats);
322
323 assert!(!result.provable);
324 assert_eq!(result.missing_facts.len(), 2);
325 assert!(result.bindings.is_empty());
326 }
327
328 #[test]
329 fn test_proof_trace_from_goal() {
330 let mut goal = Goal::new("User.IsVIP == true".to_string());
331 goal.depth = 1;
332 goal.add_candidate_rule("VIPRule".to_string());
333
334 let mut subgoal = Goal::new("User.Points > 1000".to_string());
335 subgoal.depth = 2;
336 subgoal.add_candidate_rule("PointsRule".to_string());
337
338 goal.add_subgoal(subgoal);
339
340 let trace = ProofTrace::from_goal(&goal);
341
342 assert_eq!(trace.goal, "User.IsVIP == true");
343 assert_eq!(trace.steps.len(), 1);
344 assert_eq!(trace.steps[0].rule_name, "VIPRule");
345 assert_eq!(trace.steps[0].sub_steps.len(), 1);
346 }
347
348 #[test]
349 fn test_proof_step_nested() {
350 let sub_step = ProofStep {
351 rule_name: "SubRule".to_string(),
352 goal: "subgoal".to_string(),
353 sub_steps: Vec::new(),
354 depth: 2,
355 };
356
357 let step = ProofStep {
358 rule_name: "MainRule".to_string(),
359 goal: "main".to_string(),
360 sub_steps: vec![sub_step],
361 depth: 1,
362 };
363
364 assert_eq!(step.sub_steps.len(), 1);
365 assert_eq!(step.sub_steps[0].rule_name, "SubRule");
366 assert_eq!(step.sub_steps[0].depth, 2);
367 }
368
369 #[test]
370 fn test_query_parser_complex_expressions() {
371 let and_result = QueryParser::parse("User.IsVIP == true && Order.Total > 1000");
373 assert!(and_result.is_ok());
374
375 let or_result = QueryParser::parse("User.Points > 500 || User.IsVIP == true");
377 assert!(or_result.is_ok());
378
379 let not_result = QueryParser::parse("!(User.IsBanned == true)");
381 assert!(not_result.is_ok());
382 }
383
384 #[test]
385 fn test_query_parser_invalid_syntax() {
386 assert!(QueryParser::parse("").is_err());
388
389 assert!(QueryParser::parse("(User.IsVIP == true").is_err());
391
392 assert!(QueryParser::parse("User.IsVIP == == true").is_err());
394 }
395
396 #[test]
397 fn test_proof_trace_empty() {
398 let trace = ProofTrace::empty();
399 assert!(trace.goal.is_empty());
400 assert!(trace.steps.is_empty());
401 }
402}