use std::collections::HashMap;
use std::fmt::Formatter;
use std::sync::Arc;
use std::{cmp, fmt};
use crate::{DisplayFormatType, ExecutionPlan};
pub struct Coordinate {
#[expect(dead_code)]
pub x: usize,
#[expect(dead_code)]
pub y: usize,
}
impl Coordinate {
pub fn new(x: usize, y: usize) -> Self {
Coordinate { x, y }
}
}
pub struct RenderTreeNode {
pub name: String,
pub extra_text: HashMap<String, String>,
pub child_positions: Vec<Coordinate>,
}
impl RenderTreeNode {
pub fn new(name: String, extra_text: HashMap<String, String>) -> Self {
RenderTreeNode {
name,
extra_text,
child_positions: vec![],
}
}
fn add_child_position(&mut self, x: usize, y: usize) {
self.child_positions.push(Coordinate::new(x, y));
}
}
pub struct RenderTree {
pub nodes: Vec<Option<Arc<RenderTreeNode>>>,
pub width: usize,
pub height: usize,
}
impl RenderTree {
pub fn create_tree(plan: &dyn ExecutionPlan) -> Self {
let (width, height) = get_tree_width_height(plan);
let mut result = Self::new(width, height);
create_tree_recursive(&mut result, plan, 0, 0);
result
}
fn new(width: usize, height: usize) -> Self {
RenderTree {
nodes: vec![None; (width + 1) * (height + 1)],
width,
height,
}
}
pub fn get_node(&self, x: usize, y: usize) -> Option<Arc<RenderTreeNode>> {
if x >= self.width || y >= self.height {
return None;
}
let pos = self.get_position(x, y);
self.nodes.get(pos).and_then(|node| node.clone())
}
pub fn set_node(&mut self, x: usize, y: usize, node: Arc<RenderTreeNode>) {
let pos = self.get_position(x, y);
if let Some(slot) = self.nodes.get_mut(pos) {
*slot = Some(node);
}
}
pub fn has_node(&self, x: usize, y: usize) -> bool {
if x >= self.width || y >= self.height {
return false;
}
let pos = self.get_position(x, y);
self.nodes.get(pos).is_some_and(|node| node.is_some())
}
fn get_position(&self, x: usize, y: usize) -> usize {
y * self.width + x
}
}
fn get_tree_width_height(plan: &dyn ExecutionPlan) -> (usize, usize) {
let children = plan.children();
if children.is_empty() {
return (1, 1);
}
let mut width = 0;
let mut height = 0;
for child in children {
let (child_width, child_height) = get_tree_width_height(child.as_ref());
width += child_width;
height = cmp::max(height, child_height);
}
height += 1;
(width, height)
}
fn fmt_display(plan: &dyn ExecutionPlan) -> impl fmt::Display + '_ {
struct Wrapper<'a> {
plan: &'a dyn ExecutionPlan,
}
impl fmt::Display for Wrapper<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.plan.fmt_as(DisplayFormatType::TreeRender, f)?;
Ok(())
}
}
Wrapper { plan }
}
fn create_tree_recursive(
result: &mut RenderTree,
plan: &dyn ExecutionPlan,
x: usize,
y: usize,
) -> usize {
let display_info = fmt_display(plan).to_string();
let mut extra_info = HashMap::new();
for line in display_info.lines() {
if let Some((key, value)) = line.split_once('=') {
extra_info.insert(key.to_string(), value.to_string());
} else {
extra_info.insert(line.to_string(), "".to_string());
}
}
let mut node = RenderTreeNode::new(plan.name().to_string(), extra_info);
let children = plan.children();
if children.is_empty() {
result.set_node(x, y, Arc::new(node));
return 1;
}
let mut width = 0;
for child in children {
let child_x = x + width;
let child_y = y + 1;
node.add_child_position(child_x, child_y);
width += create_tree_recursive(result, child.as_ref(), child_x, child_y);
}
result.set_node(x, y, Arc::new(node));
width
}