1use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::time::{Duration, Instant};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16pub enum ExecutionPhase {
17 Parsing,
19 Validation,
21 Translation,
23 Execution,
25 Resolution,
27 Formatting,
29}
30
31impl std::fmt::Display for ExecutionPhase {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 ExecutionPhase::Parsing => write!(f, "Parsing"),
35 ExecutionPhase::Validation => write!(f, "Validation"),
36 ExecutionPhase::Translation => write!(f, "Translation"),
37 ExecutionPhase::Execution => write!(f, "Execution"),
38 ExecutionPhase::Resolution => write!(f, "Resolution"),
39 ExecutionPhase::Formatting => write!(f, "Formatting"),
40 }
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct PhaseTimings {
47 pub phase: ExecutionPhase,
48 pub duration: Duration,
49 pub start_time: u64,
50 pub end_time: u64,
51}
52
53impl PhaseTimings {
54 pub fn new(phase: ExecutionPhase, duration: Duration, start_time: u64) -> Self {
55 Self {
56 phase,
57 duration,
58 start_time,
59 end_time: start_time + duration.as_millis() as u64,
60 }
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ExecutionTrace {
67 pub query: String,
69 pub variables: HashMap<String, serde_json::Value>,
71 pub timings: Vec<PhaseTimings>,
73 pub total_duration: Duration,
75 pub sparql_queries: Vec<String>,
77 pub result_count: usize,
79 pub errors: Vec<String>,
81 pub warnings: Vec<String>,
83}
84
85impl ExecutionTrace {
86 pub fn new(query: String) -> Self {
87 Self {
88 query,
89 variables: HashMap::new(),
90 timings: Vec::new(),
91 total_duration: Duration::from_secs(0),
92 sparql_queries: Vec::new(),
93 result_count: 0,
94 errors: Vec::new(),
95 warnings: Vec::new(),
96 }
97 }
98
99 pub fn add_timing(&mut self, phase: ExecutionPhase, duration: Duration, start_time: u64) {
100 self.timings
101 .push(PhaseTimings::new(phase, duration, start_time));
102 }
103
104 pub fn add_sparql_query(&mut self, query: String) {
105 self.sparql_queries.push(query);
106 }
107
108 pub fn add_error(&mut self, error: String) {
109 self.errors.push(error);
110 }
111
112 pub fn add_warning(&mut self, warning: String) {
113 self.warnings.push(warning);
114 }
115
116 pub fn generate_report(&self) -> String {
118 let mut report = String::new();
119
120 report.push_str("=== Query Execution Trace ===\n\n");
121
122 report.push_str("Query:\n");
124 report.push_str(&format!("{}\n\n", self.query));
125
126 if !self.variables.is_empty() {
128 report.push_str("Variables:\n");
129 for (key, value) in &self.variables {
130 report.push_str(&format!(" {}: {}\n", key, value));
131 }
132 report.push('\n');
133 }
134
135 report.push_str("Execution Timings:\n");
137 for timing in &self.timings {
138 report.push_str(&format!(
139 " {:<15} {:>8.2}ms\n",
140 format!("{}:", timing.phase),
141 timing.duration.as_secs_f64() * 1000.0
142 ));
143 }
144 report.push_str(&format!(
145 " {:<15} {:>8.2}ms\n\n",
146 "Total:",
147 self.total_duration.as_secs_f64() * 1000.0
148 ));
149
150 if !self.sparql_queries.is_empty() {
152 report.push_str("Generated SPARQL Queries:\n");
153 for (idx, query) in self.sparql_queries.iter().enumerate() {
154 report.push_str(&format!("\nQuery #{}:\n", idx + 1));
155 report.push_str(&format!("{}\n", query));
156 }
157 report.push('\n');
158 }
159
160 report.push_str(&format!("Results: {} records\n\n", self.result_count));
162
163 if !self.errors.is_empty() {
165 report.push_str("Errors:\n");
166 for error in &self.errors {
167 report.push_str(&format!(" - {}\n", error));
168 }
169 report.push('\n');
170 }
171
172 if !self.warnings.is_empty() {
174 report.push_str("Warnings:\n");
175 for warning in &self.warnings {
176 report.push_str(&format!(" - {}\n", warning));
177 }
178 report.push('\n');
179 }
180
181 report
182 }
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct ComplexityMetrics {
188 pub total_score: u32,
190 pub max_depth: u32,
192 pub field_count: u32,
194 pub list_count: u32,
196 pub fragment_count: u32,
198 pub field_scores: HashMap<String, u32>,
200}
201
202impl ComplexityMetrics {
203 pub fn new() -> Self {
204 Self {
205 total_score: 0,
206 max_depth: 0,
207 field_count: 0,
208 list_count: 0,
209 fragment_count: 0,
210 field_scores: HashMap::new(),
211 }
212 }
213
214 pub fn add_field_score(&mut self, field: String, score: u32) {
215 self.field_scores.insert(field, score);
216 self.total_score += score;
217 }
218
219 pub fn exceeds_threshold(&self, threshold: u32) -> bool {
221 self.total_score > threshold
222 }
223
224 pub fn generate_report(&self) -> String {
226 let mut report = String::new();
227
228 report.push_str("=== Query Complexity Analysis ===\n\n");
229 report.push_str(&format!("Total Complexity Score: {}\n", self.total_score));
230 report.push_str(&format!("Maximum Depth: {}\n", self.max_depth));
231 report.push_str(&format!("Field Count: {}\n", self.field_count));
232 report.push_str(&format!("List Count: {}\n", self.list_count));
233 report.push_str(&format!("Fragment Count: {}\n\n", self.fragment_count));
234
235 if !self.field_scores.is_empty() {
236 report.push_str("Field-level Complexity:\n");
237 let mut sorted_fields: Vec<_> = self.field_scores.iter().collect();
238 sorted_fields.sort_by(|a, b| b.1.cmp(a.1));
239
240 for (field, score) in sorted_fields.iter().take(10) {
241 report.push_str(&format!(" {:<30} {}\n", field, score));
242 }
243 }
244
245 report
246 }
247}
248
249impl Default for ComplexityMetrics {
250 fn default() -> Self {
251 Self::new()
252 }
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct QueryExplanation {
258 pub query: String,
260 pub plan_steps: Vec<PlanStep>,
262 pub estimated_cost: f64,
264 pub recommendations: Vec<String>,
266}
267
268impl QueryExplanation {
269 pub fn new(query: String) -> Self {
270 Self {
271 query,
272 plan_steps: Vec::new(),
273 estimated_cost: 0.0,
274 recommendations: Vec::new(),
275 }
276 }
277
278 pub fn add_step(&mut self, step: PlanStep) {
279 self.estimated_cost += step.estimated_cost;
280 self.plan_steps.push(step);
281 }
282
283 pub fn add_recommendation(&mut self, recommendation: String) {
284 self.recommendations.push(recommendation);
285 }
286
287 pub fn generate_report(&self) -> String {
289 let mut report = String::new();
290
291 report.push_str("=== Query Execution Plan ===\n\n");
292 report.push_str("Query:\n");
293 report.push_str(&format!("{}\n\n", self.query));
294
295 report.push_str("Execution Steps:\n");
296 for (idx, step) in self.plan_steps.iter().enumerate() {
297 report.push_str(&format!(
298 "{}. {} (cost: {:.2})\n",
299 idx + 1,
300 step.description,
301 step.estimated_cost
302 ));
303 if !step.details.is_empty() {
304 report.push_str(&format!(" {}\n", step.details));
305 }
306 }
307
308 report.push_str(&format!(
309 "\nTotal Estimated Cost: {:.2}\n\n",
310 self.estimated_cost
311 ));
312
313 if !self.recommendations.is_empty() {
314 report.push_str("Optimization Recommendations:\n");
315 for (idx, rec) in self.recommendations.iter().enumerate() {
316 report.push_str(&format!(" {}. {}\n", idx + 1, rec));
317 }
318 }
319
320 report
321 }
322}
323
324#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct PlanStep {
327 pub description: String,
329 pub details: String,
331 pub estimated_cost: f64,
333 pub expected_results: Option<usize>,
335}
336
337impl PlanStep {
338 pub fn new(description: String, estimated_cost: f64) -> Self {
339 Self {
340 description,
341 details: String::new(),
342 estimated_cost,
343 expected_results: None,
344 }
345 }
346
347 pub fn with_details(mut self, details: String) -> Self {
348 self.details = details;
349 self
350 }
351
352 pub fn with_expected_results(mut self, count: usize) -> Self {
353 self.expected_results = Some(count);
354 self
355 }
356}
357
358pub struct QueryDebugger {
360 pub enable_tracing: bool,
362 pub enable_profiling: bool,
364 pub enable_sparql_preview: bool,
366 pub complexity_threshold: u32,
368}
369
370impl QueryDebugger {
371 pub fn new() -> Self {
372 Self {
373 enable_tracing: true,
374 enable_profiling: true,
375 enable_sparql_preview: true,
376 complexity_threshold: 1000,
377 }
378 }
379
380 pub fn with_tracing(mut self, enabled: bool) -> Self {
381 self.enable_tracing = enabled;
382 self
383 }
384
385 pub fn with_profiling(mut self, enabled: bool) -> Self {
386 self.enable_profiling = enabled;
387 self
388 }
389
390 pub fn with_sparql_preview(mut self, enabled: bool) -> Self {
391 self.enable_sparql_preview = enabled;
392 self
393 }
394
395 pub fn with_complexity_threshold(mut self, threshold: u32) -> Self {
396 self.complexity_threshold = threshold;
397 self
398 }
399
400 pub fn start_trace(&self, query: String) -> ExecutionTrace {
402 ExecutionTrace::new(query)
403 }
404
405 pub fn analyze_complexity(&self, _query: &str) -> ComplexityMetrics {
407 ComplexityMetrics::new()
410 }
411
412 pub fn explain_query(&self, query: String) -> QueryExplanation {
414 let mut explanation = QueryExplanation::new(query);
415
416 explanation.add_step(
418 PlanStep::new("Parse GraphQL query".to_string(), 1.0)
419 .with_details("Parse query into AST".to_string()),
420 );
421
422 explanation.add_step(
423 PlanStep::new("Validate query".to_string(), 2.0)
424 .with_details("Validate against schema".to_string()),
425 );
426
427 explanation.add_step(
428 PlanStep::new("Translate to SPARQL".to_string(), 3.0)
429 .with_details("Generate SPARQL query from GraphQL".to_string()),
430 );
431
432 explanation.add_step(
433 PlanStep::new("Execute SPARQL query".to_string(), 10.0)
434 .with_details("Execute against RDF store".to_string())
435 .with_expected_results(100),
436 );
437
438 explanation
439 .add_recommendation("Consider adding pagination to limit result set".to_string());
440
441 explanation
442 }
443}
444
445impl Default for QueryDebugger {
446 fn default() -> Self {
447 Self::new()
448 }
449}
450
451pub struct PhaseTimer {
453 start: Instant,
454 phase: ExecutionPhase,
455}
456
457impl PhaseTimer {
458 pub fn start(phase: ExecutionPhase) -> Self {
459 Self {
460 start: Instant::now(),
461 phase,
462 }
463 }
464
465 pub fn finish(self) -> (ExecutionPhase, Duration) {
466 (self.phase, self.start.elapsed())
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 #[test]
475 fn test_execution_phase_display() {
476 assert_eq!(ExecutionPhase::Parsing.to_string(), "Parsing");
477 assert_eq!(ExecutionPhase::Validation.to_string(), "Validation");
478 assert_eq!(ExecutionPhase::Execution.to_string(), "Execution");
479 }
480
481 #[test]
482 fn test_execution_trace() {
483 let mut trace = ExecutionTrace::new("{ user { name } }".to_string());
484 trace.add_timing(ExecutionPhase::Parsing, Duration::from_millis(10), 0);
485 trace.add_timing(ExecutionPhase::Execution, Duration::from_millis(50), 10);
486 trace.add_sparql_query("SELECT ?name WHERE { ?user foaf:name ?name }".to_string());
487 trace.result_count = 42;
488
489 assert_eq!(trace.timings.len(), 2);
490 assert_eq!(trace.sparql_queries.len(), 1);
491 assert_eq!(trace.result_count, 42);
492 }
493
494 #[test]
495 fn test_execution_trace_report() {
496 let mut trace = ExecutionTrace::new("{ user { name } }".to_string());
497 trace.add_timing(ExecutionPhase::Parsing, Duration::from_millis(10), 0);
498 trace.total_duration = Duration::from_millis(100);
499
500 let report = trace.generate_report();
501 assert!(report.contains("Query Execution Trace"));
502 assert!(report.contains("user"));
503 assert!(report.contains("Parsing"));
504 }
505
506 #[test]
507 fn test_complexity_metrics() {
508 let mut metrics = ComplexityMetrics::new();
509 metrics.add_field_score("user".to_string(), 10);
510 metrics.add_field_score("posts".to_string(), 20);
511 metrics.max_depth = 3;
512 metrics.field_count = 5;
513
514 assert_eq!(metrics.total_score, 30);
515 assert_eq!(metrics.field_count, 5);
516 assert!(!metrics.exceeds_threshold(100));
517 assert!(metrics.exceeds_threshold(20));
518 }
519
520 #[test]
521 fn test_complexity_report() {
522 let mut metrics = ComplexityMetrics::new();
523 metrics.add_field_score("user.posts".to_string(), 50);
524 metrics.total_score = 50;
525
526 let report = metrics.generate_report();
527 assert!(report.contains("Complexity Analysis"));
528 assert!(report.contains("50"));
529 }
530
531 #[test]
532 fn test_query_explanation() {
533 let mut explanation = QueryExplanation::new("{ user { name } }".to_string());
534 explanation.add_step(PlanStep::new("Parse query".to_string(), 1.0));
535 explanation.add_step(PlanStep::new("Execute query".to_string(), 10.0));
536 explanation.add_recommendation("Use caching".to_string());
537
538 assert_eq!(explanation.plan_steps.len(), 2);
539 assert_eq!(explanation.estimated_cost, 11.0);
540 assert_eq!(explanation.recommendations.len(), 1);
541 }
542
543 #[test]
544 fn test_query_explanation_report() {
545 let mut explanation = QueryExplanation::new("{ user { name } }".to_string());
546 explanation.add_step(PlanStep::new("Parse".to_string(), 1.0));
547
548 let report = explanation.generate_report();
549 assert!(report.contains("Execution Plan"));
550 assert!(report.contains("Parse"));
551 }
552
553 #[test]
554 fn test_plan_step() {
555 let step = PlanStep::new("Execute SPARQL".to_string(), 10.0)
556 .with_details("Query RDF store".to_string())
557 .with_expected_results(100);
558
559 assert_eq!(step.description, "Execute SPARQL");
560 assert_eq!(step.estimated_cost, 10.0);
561 assert_eq!(step.expected_results, Some(100));
562 }
563
564 #[test]
565 fn test_query_debugger_creation() {
566 let debugger = QueryDebugger::new()
567 .with_tracing(true)
568 .with_profiling(true)
569 .with_complexity_threshold(500);
570
571 assert!(debugger.enable_tracing);
572 assert!(debugger.enable_profiling);
573 assert_eq!(debugger.complexity_threshold, 500);
574 }
575
576 #[test]
577 fn test_query_debugger_trace() {
578 let debugger = QueryDebugger::new();
579 let trace = debugger.start_trace("{ user { name } }".to_string());
580
581 assert_eq!(trace.query, "{ user { name } }");
582 assert_eq!(trace.timings.len(), 0);
583 }
584
585 #[test]
586 fn test_query_debugger_complexity() {
587 let debugger = QueryDebugger::new();
588 let metrics = debugger.analyze_complexity("{ user { name } }");
589
590 assert_eq!(metrics.total_score, 0); }
592
593 #[test]
594 fn test_query_debugger_explain() {
595 let debugger = QueryDebugger::new();
596 let explanation = debugger.explain_query("{ user { name } }".to_string());
597
598 assert!(!explanation.plan_steps.is_empty());
599 assert!(explanation.estimated_cost > 0.0);
600 }
601
602 #[test]
603 fn test_phase_timer() {
604 let timer = PhaseTimer::start(ExecutionPhase::Parsing);
605 std::thread::sleep(Duration::from_millis(10));
606 let (phase, duration) = timer.finish();
607
608 assert_eq!(phase, ExecutionPhase::Parsing);
609 assert!(duration.as_millis() >= 10);
610 }
611}