1use super::report::QueryInspection;
2use std::fmt::Write;
3
4impl QueryInspection {
5 pub fn format_tree(&self) -> String {
7 let mut out = String::new();
8 let _ = writeln!(out, "query:");
9 let _ = writeln!(out, " {}", self.query);
10 let _ = writeln!(out, "context:");
11 let _ = writeln!(out, " {:?} / {:?}", self.context, self.level);
12 let _ = writeln!(out, "summary:");
13 let _ = writeln!(out, " root: {}", self.summary.root);
14 let _ = writeln!(
15 out,
16 " selected executor: {}",
17 self.summary
18 .selected_executor
19 .map(|backend| format!("{backend:?}"))
20 .unwrap_or_else(|| "unknown".to_string())
21 );
22 let _ = writeln!(
23 out,
24 " materializes root: {}",
25 self.summary.materializes_root
26 );
27 let _ = writeln!(
28 out,
29 " contains vm fallback: {}",
30 self.summary.contains_vm_fallback
31 );
32 let _ = writeln!(out, " byte native: {}", self.summary.can_run_byte_native);
33
34 if let Some(logical) = &self.logical {
35 let _ = writeln!(out, "logical:");
36 let _ = writeln!(out, " ast root: {}", logical.ast_root);
37 let _ = writeln!(out, " root shape: {}", logical.root_shape);
38 for note in &logical.notes {
39 let _ = writeln!(out, " note: {note}");
40 }
41 }
42
43 if let Some(physical) = &self.physical {
44 let _ = writeln!(out, "physical:");
45 let _ = writeln!(out, " root: {}", physical.root);
46 for node in &physical.nodes {
47 let _ = writeln!(out, " node {}: {}", node.id, node.kind);
48 if !node.children.is_empty() {
49 let _ = writeln!(out, " children: {:?}", node.children);
50 }
51 if let Some(detail) = &node.detail {
52 let _ = writeln!(out, " detail: {detail}");
53 }
54 let _ = writeln!(
55 out,
56 " facts: byte_native={}, avoid_root_materialization={}, vm_fallback={}",
57 node.facts.can_run_byte_native,
58 node.facts.can_avoid_root_materialization,
59 node.facts.contains_vm_fallback
60 );
61 if !node.backends.is_empty() {
62 let backends = node
63 .backends
64 .iter()
65 .map(|backend| format!("{:?}:{:?}", backend.backend, backend.status))
66 .collect::<Vec<_>>()
67 .join(", ");
68 let _ = writeln!(out, " backends: {backends}");
69 }
70 }
71 }
72
73 if let Some(pipeline) = &self.pipeline {
74 let _ = writeln!(out, "pipeline:");
75 let _ = writeln!(out, " source: {}", pipeline.source);
76 let _ = writeln!(out, " demand: {}", pipeline.source_demand);
77 let _ = writeln!(out, " sink: {}", pipeline.sink);
78 if let Some(path) = &pipeline.execution_path {
79 let _ = writeln!(out, " execution path: {path}");
80 }
81 if let Some(boundary) = &pipeline.fallback_boundary {
82 let _ = writeln!(out, " fallback boundary: {boundary}");
83 }
84 for stage in &pipeline.stages {
85 let _ = writeln!(out, " stage {}: {}", stage.index, stage.kind);
86 if let Some(detail) = &stage.detail {
87 let _ = writeln!(out, " {detail}");
88 }
89 }
90 }
91
92 if let Some(ndjson) = &self.ndjson {
93 let _ = writeln!(out, "ndjson:");
94 let _ = writeln!(out, " route: {}", ndjson.route_kind);
95 let _ = writeln!(out, " source: {}", ndjson.source);
96 if let Some(reason) = &ndjson.fallback_reason {
97 let _ = writeln!(out, " fallback: {reason}");
98 }
99 if let Some(path) = &ndjson.writer_path {
100 let _ = writeln!(out, " writer path: {path}");
101 }
102 if let Some(rows) = &ndjson.rows {
103 let _ = writeln!(out, " rows:");
104 let _ = writeln!(out, " plan: {}", rows.plan_kind);
105 let _ = writeln!(out, " source: {}", rows.source);
106 let _ = writeln!(out, " direction: {}", rows.direction);
107 let _ = writeln!(out, " demand: {}", rows.demand);
108 if let Some(strategy) = &rows.file_strategy {
109 let _ = writeln!(out, " file strategy: {strategy}");
110 }
111 let _ = writeln!(out, " sink: {}", rows.sink);
112 for stage in &rows.stages {
113 let _ = writeln!(out, " stage {}: {}", stage.index, stage.kind);
114 if let Some(detail) = &stage.detail {
115 let _ = writeln!(out, " {detail}");
116 }
117 }
118 }
119 for plan in &ndjson.direct_plans {
120 let _ = writeln!(out, " direct: {}", plan.kind);
121 if let Some(detail) = &plan.detail {
122 let _ = writeln!(out, " {detail}");
123 }
124 }
125 }
126
127 if !self.warnings.is_empty() {
128 let _ = writeln!(out, "warnings:");
129 for warning in &self.warnings {
130 let _ = writeln!(out, " {warning}");
131 }
132 }
133 out
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use crate::introspect::{
140 BackendKind, InspectContext, InspectLevel, InspectionSummary, QueryInspection,
141 };
142
143 #[test]
144 fn tree_renderer_includes_key_sections() {
145 let report = QueryInspection {
146 query: "$.a".to_string(),
147 context: InspectContext::Bytes,
148 level: InspectLevel::Plan,
149 summary: InspectionSummary {
150 root: "node:0".to_string(),
151 selected_executor: Some(BackendKind::TapePath),
152 materializes_root: false,
153 contains_vm_fallback: false,
154 can_run_byte_native: true,
155 },
156 logical: None,
157 physical: None,
158 pipeline: None,
159 ndjson: None,
160 warnings: Vec::new(),
161 };
162
163 let text = report.format_tree();
164
165 assert!(text.contains("query:"));
166 assert!(text.contains("summary:"));
167 assert!(text.contains("TapePath"));
168 }
169}