render-tree 0.1.1

A library for rendering trees of content to the terminal
Documentation
use crate::stylesheet::WriteStyle;
use crate::Document;
use crate::{Node, PadItem};
use crate::{Style, Stylesheet};
use std::{fmt, io};
use termcolor::WriteColor;

struct DebugDocument<'a, C: WriteColor + 'a> {
    document: &'a Document,
    writer: &'a mut C,
    stylesheet: &'a Stylesheet,
    line_start: bool,
    nesting: Vec<&'static str>,
}

impl<'a, C: WriteColor + 'a> DebugDocument<'a, C> {
    fn write_document(mut self) -> io::Result<()> {
        let tree = match self.document.tree() {
            None => return Ok(()),
            Some(nodes) => nodes,
        };

        self.writer.reset()?;

        for item in tree.clone() {
            match item {
                Node::Text(string) => self.write_text(string)?,
                Node::OpenSection(section) => self.write_open_section(section)?,
                Node::CloseSection => self.write_close_section()?,
                Node::Newline => self.write_newline()?,
            }
        }

        write!(self.writer, "\n\n")?;

        Ok(())
    }

    fn write_text(&mut self, string: &str) -> io::Result<()> {
        if self.line_start {
            self.start_line()?;
            self.styled_write("|", "fg: black; weight: bold")?;
        }

        self.write(string)?;
        self.line_start = false;

        Ok(())
    }

    fn write_open_section(&mut self, section: &'static str) -> io::Result<()> {
        self.start_line()?;
        self.write("<")?;

        self.nesting.push(section);
        let style = self.stylesheet.get(&self.nesting[..]);

        self.styled_write(section, "fg: blue; weight: bold")?;

        if let Some(style) = style {
            if style.has_value() {
                self.write(" ")?;
                let debug_attributes = style.debug_attributes();
                let last = debug_attributes.len() - 1;

                for (i, (name, value)) in debug_attributes.iter().enumerate() {
                    self.styled_write(name, "fg: black; weight: bold")?;

                    if let Some(value) = value {
                        self.write("=")?;
                        self.styled_write(value, "fg: cyan; weight: dim")?;
                    }

                    if i != last {
                        self.write(" ")?;
                    }
                }

                self.styled_write(" ยง", style)?;
            }
        }

        self.writer.reset()?;
        write!(self.writer, ">")?;
        self.line_start = true;

        Ok(())
    }

    fn write_close_section(&mut self) -> io::Result<()> {
        let popped = self.nesting.pop().expect("unbalanced push/pop");
        self.start_line()?;
        write!(self.writer, "</")?;

        self.writer.set_style(&Style("fg: blue; weight: bold"))?;
        write!(self.writer, "{}", popped)?;
        self.writer.reset()?;
        write!(self.writer, ">")?;
        self.line_start = true;

        Ok(())
    }

    fn write_newline(&mut self) -> io::Result<()> {
        let writer = &mut self.writer;
        writer.reset()?;

        if self.line_start {
            write!(writer, "\n{}", PadItem("  ", self.nesting.len()))?;
        }

        write!(writer, "\\n",)?;
        self.line_start = true;

        Ok(())
    }

    fn start_line(&mut self) -> io::Result<()> {
        let pad = self.pad();
        write!(self.writer, "\n{}", pad)
    }

    fn styled_write(
        &mut self,
        value: impl fmt::Display,
        style: impl Into<Style>,
    ) -> io::Result<()> {
        self.writer.set_style(style)?;
        self.write(value)?;
        self.writer.reset()
    }

    fn write(&mut self, value: impl fmt::Display) -> io::Result<()> {
        write!(self.writer, "{}", value)
    }

    fn pad(&self) -> PadItem<&'static str> {
        PadItem(" ", self.nesting.len())
    }
}

impl Document {
    pub fn debug_write(
        &self,
        writer: &mut impl WriteColor,
        stylesheet: &Stylesheet,
    ) -> io::Result<()> {
        DebugDocument {
            document: self,
            writer,
            stylesheet,
            line_start: true,
            nesting: vec![],
        }.write_document()
    }
}