datafusion_physical_plan/
render_tree.rs1use std::collections::HashMap;
25use std::fmt::Formatter;
26use std::sync::Arc;
27use std::{cmp, fmt};
28
29use crate::{DisplayFormatType, ExecutionPlan};
30
31#[allow(dead_code)]
35pub struct Coordinate {
36 pub x: usize,
38 pub y: usize,
40}
41
42impl Coordinate {
43 pub fn new(x: usize, y: usize) -> Self {
44 Coordinate { x, y }
45 }
46}
47
48pub struct RenderTreeNode {
51 pub name: String,
53 pub extra_text: HashMap<String, String>,
55 pub child_positions: Vec<Coordinate>,
57}
58
59impl RenderTreeNode {
60 pub fn new(name: String, extra_text: HashMap<String, String>) -> Self {
61 RenderTreeNode {
62 name,
63 extra_text,
64 child_positions: vec![],
65 }
66 }
67
68 fn add_child_position(&mut self, x: usize, y: usize) {
69 self.child_positions.push(Coordinate::new(x, y));
70 }
71}
72
73pub struct RenderTree {
76 pub nodes: Vec<Option<Arc<RenderTreeNode>>>,
78 pub width: usize,
80 pub height: usize,
82}
83
84impl RenderTree {
85 pub fn create_tree(plan: &dyn ExecutionPlan) -> Self {
87 let (width, height) = get_tree_width_height(plan);
88
89 let mut result = Self::new(width, height);
90
91 create_tree_recursive(&mut result, plan, 0, 0);
92
93 result
94 }
95
96 fn new(width: usize, height: usize) -> Self {
97 RenderTree {
98 nodes: vec![None; (width + 1) * (height + 1)],
99 width,
100 height,
101 }
102 }
103
104 pub fn get_node(&self, x: usize, y: usize) -> Option<Arc<RenderTreeNode>> {
105 if x >= self.width || y >= self.height {
106 return None;
107 }
108
109 let pos = self.get_position(x, y);
110 self.nodes.get(pos).and_then(|node| node.clone())
111 }
112
113 pub fn set_node(&mut self, x: usize, y: usize, node: Arc<RenderTreeNode>) {
114 let pos = self.get_position(x, y);
115 if let Some(slot) = self.nodes.get_mut(pos) {
116 *slot = Some(node);
117 }
118 }
119
120 pub fn has_node(&self, x: usize, y: usize) -> bool {
121 if x >= self.width || y >= self.height {
122 return false;
123 }
124
125 let pos = self.get_position(x, y);
126 self.nodes.get(pos).is_some_and(|node| node.is_some())
127 }
128
129 fn get_position(&self, x: usize, y: usize) -> usize {
130 y * self.width + x
131 }
132}
133
134fn get_tree_width_height(plan: &dyn ExecutionPlan) -> (usize, usize) {
143 let children = plan.children();
144
145 if children.is_empty() {
147 return (1, 1);
148 }
149
150 let mut width = 0;
151 let mut height = 0;
152
153 for child in children {
154 let (child_width, child_height) = get_tree_width_height(child.as_ref());
155 width += child_width;
156 height = cmp::max(height, child_height);
157 }
158
159 height += 1;
160
161 (width, height)
162}
163
164fn fmt_display(plan: &dyn ExecutionPlan) -> impl fmt::Display + '_ {
165 struct Wrapper<'a> {
166 plan: &'a dyn ExecutionPlan,
167 }
168
169 impl fmt::Display for Wrapper<'_> {
170 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
171 self.plan.fmt_as(DisplayFormatType::TreeRender, f)?;
172 Ok(())
173 }
174 }
175
176 Wrapper { plan }
177}
178
179fn create_tree_recursive(
192 result: &mut RenderTree,
193 plan: &dyn ExecutionPlan,
194 x: usize,
195 y: usize,
196) -> usize {
197 let display_info = fmt_display(plan).to_string();
198 let mut extra_info = HashMap::new();
199
200 for line in display_info.lines() {
203 if let Some((key, value)) = line.split_once('=') {
204 extra_info.insert(key.to_string(), value.to_string());
205 } else {
206 extra_info.insert(line.to_string(), "".to_string());
207 }
208 }
209
210 let mut node = RenderTreeNode::new(plan.name().to_string(), extra_info);
211
212 let children = plan.children();
213
214 if children.is_empty() {
215 result.set_node(x, y, Arc::new(node));
216 return 1;
217 }
218
219 let mut width = 0;
220 for child in children {
221 let child_x = x + width;
222 let child_y = y + 1;
223 node.add_child_position(child_x, child_y);
224 width += create_tree_recursive(result, child.as_ref(), child_x, child_y);
225 }
226
227 result.set_node(x, y, Arc::new(node));
228
229 width
230}