display-node 0.1.0

Tiny crate adding a trait to display recursive structures like node trees.
Documentation
use std::vec;

pub mod implementations;

/// simple trait to provide an easy way to display hierarchy trees
pub trait DisplayableNode: Sized {
    fn label(&self) -> Option<String>;
    fn children(&self) -> Vec<&Self>;

    fn display(&self) -> String {
        NodeDisplayBlock::from_displayable_node(self, 0).extract()
    }
}

///
struct NodeDisplayBlock {
    lines: Vec<String>,
}

impl NodeDisplayBlock {
    fn extract(self) -> String {
        self.lines.join("\n")
    }

    fn from_displayable_node<N: DisplayableNode>(node: &N, index: usize) -> Self {
        let mut result = Self::from_label(node, index);
        let children = node.children();
        result.append_children::<N>(children);

        result
    }

    fn from_label<N: DisplayableNode>(node: &N, index: usize) -> Self {
        let mut lines = Vec::new();
        let label = node.label().unwrap_or_else(|| format!("[{index}]"));

        for line in label.lines() {
            lines.push(line.to_string());
        }

        Self { lines }
    }

    fn append_children<N: DisplayableNode>(&mut self, children: Vec<&N>) {
        if children.is_empty() {
            return;
        }
        let last_child_index = children.len() - 1;
        let mut children_iterator = children.into_iter();
        self.append_middle_children_nodes(&mut children_iterator, last_child_index);
        let last_child_node = children_iterator.next().unwrap();
        self.append_last_child_node(last_child_node, last_child_index);
    }

    fn append_middle_children_nodes<N: DisplayableNode>(
        &mut self,
        children_iterator: &mut vec::IntoIter<&N>,
        last_child_index: usize,
    ) {
        for middle_child_index in 0..last_child_index {
            let middle_child_node = children_iterator.next().unwrap();
            let middle_child_block =
                Self::from_displayable_node(middle_child_node, middle_child_index);
            self.append_middle_child_block(&middle_child_block);
        }
    }

    fn append_last_child_node<N: DisplayableNode>(
        &mut self,
        last_child_node: &N,
        last_child_index: usize,
    ) {
        let last_child_block = Self::from_displayable_node(last_child_node, last_child_index);
        self.append_last_child_block(&last_child_block);
    }

    fn append_middle_child_block(&mut self, middle_child: &NodeDisplayBlock) {
        let own_lines = &mut self.lines;
        let mut child_lines_iterator = middle_child.lines.iter();
        let child_label_line = child_lines_iterator.next().unwrap();
        own_lines.push(format!("├─ {child_label_line}"));
        for child_line in child_lines_iterator {
            own_lines.push(format!("{child_line}"));
        }
    }

    fn append_last_child_block(&mut self, last_child: &NodeDisplayBlock) {
        let own_lines = &mut self.lines;
        let mut child_lines_iterator = last_child.lines.iter();
        let child_label_line = child_lines_iterator.next().unwrap();
        own_lines.push(format!("└─ {child_label_line}"));
        for child_line in child_lines_iterator {
            own_lines.push(format!("   {child_line}"));
        }
    }
}