use crate::{
Buffer,
Conciliator,
Inline
};
use crate::style::{
Color,
Paint
};
use crate::data::tree;
use super::Print;
pub struct Tree<'s, I: ExactSizeIterator<Item = T>, T, F> {
root_nodes: I,
description: &'s str,
traverser: F
}
enum Chunk {
Stem,
Knot,
Tail,
None
}
impl<'s, I, T, F> Tree<'s, I, T, F>
where
I: ExactSizeIterator<Item = T>,
F: FnMut(T) -> Option<I>,
T: Inline
{
pub fn new(description: &'s str, root_nodes: I, traverser: F) -> Self {
Self {root_nodes, description, traverser}
}
fn with_last(items: I) -> impl Iterator<Item = (T, bool)> {
let last = items.len().saturating_sub(1);
items
.enumerate()
.map(move |(i, item)| (item, i == last))
}
}
impl<'s, I, T, F> Print for Tree<'s, I, T, F>
where
I: ExactSizeIterator<Item = T>,
F: FnMut(T) -> Option<I>,
T: Inline
{
fn print<C: Conciliator + ?Sized>(mut self, con: &C) {
let mut iter_stack: Vec<_> = vec![Self::with_last(self.root_nodes)];
let mut prefix_stack: Vec<Chunk> = vec![Chunk::None];
con.get_line()
.tag(Color::Omega, tree::ROOT)
.push_plain(&self.description);
'outer: while let Some(mut iter) = iter_stack.pop() {
while let Some((item, is_last)) = iter.next() {
let mut line = con.get_line();
prefix_stack.iter().for_each(|c| {line.push(c);});
line
.push(if is_last {&Chunk::Tail} else {&Chunk::Knot})
.push(&item);
if let Some(children) = (self.traverser)(item) {
prefix_stack.push(
if is_last {Chunk::None} else {Chunk::Stem}
);
iter_stack.push(iter);
iter_stack.push(Self::with_last(children));
continue 'outer;
}
}
prefix_stack.pop();
}
}
}
impl Inline for Chunk {
fn inline(&self, buffer: &mut Buffer) {
use tree::*;
match self {
Self::Stem => buffer.push_omega(STEM),
Self::Knot => buffer.push_omega(KNOT),
Self::Tail => buffer.push_omega(TAIL),
Self::None => buffer.push_plain(" ")
};
}
}