1use crate::stylesheet::WriteStyle;
2use crate::Document;
3use crate::{Node, PadItem};
4use crate::{Style, Stylesheet};
5use std::{fmt, io};
6use termcolor::WriteColor;
7
8struct DebugDocument<'a, C: WriteColor + 'a> {
9 document: &'a Document,
10 writer: &'a mut C,
11 stylesheet: &'a Stylesheet,
12 line_start: bool,
13 nesting: Vec<&'static str>,
14}
15
16impl<'a, C: WriteColor + 'a> DebugDocument<'a, C> {
17 fn write_document(mut self) -> io::Result<()> {
18 let tree = match self.document.tree() {
19 None => return Ok(()),
20 Some(nodes) => nodes,
21 };
22
23 self.writer.reset()?;
24
25 for item in tree.clone() {
26 match item {
27 Node::Text(string) => self.write_text(string)?,
28 Node::OpenSection(section) => self.write_open_section(section)?,
29 Node::CloseSection => self.write_close_section()?,
30 Node::Newline => self.write_newline()?,
31 }
32 }
33
34 write!(self.writer, "\n\n")?;
35
36 Ok(())
37 }
38
39 fn write_text(&mut self, string: &str) -> io::Result<()> {
40 if self.line_start {
41 self.start_line()?;
42 self.styled_write("|", "fg: black; weight: bold")?;
43 }
44
45 self.write(string)?;
46 self.line_start = false;
47
48 Ok(())
49 }
50
51 fn write_open_section(&mut self, section: &'static str) -> io::Result<()> {
52 self.start_line()?;
53 self.write("<")?;
54
55 self.nesting.push(section);
56 let style = self.stylesheet.get(&self.nesting[..]);
57
58 self.styled_write(section, "fg: blue; weight: bold")?;
59
60 if let Some(style) = style {
61 if style.has_value() {
62 self.write(" ")?;
63 let debug_attributes = style.debug_attributes();
64 let last = debug_attributes.len() - 1;
65
66 for (i, (name, value)) in debug_attributes.iter().enumerate() {
67 self.styled_write(name, "fg: black; weight: bold")?;
68
69 if let Some(value) = value {
70 self.write("=")?;
71 self.styled_write(value, "fg: cyan; weight: dim")?;
72 }
73
74 if i != last {
75 self.write(" ")?;
76 }
77 }
78
79 self.styled_write(" ยง", style)?;
80 }
81 }
82
83 self.writer.reset()?;
84 write!(self.writer, ">")?;
85 self.line_start = true;
86
87 Ok(())
88 }
89
90 fn write_close_section(&mut self) -> io::Result<()> {
91 let popped = self.nesting.pop().expect("unbalanced push/pop");
92 self.start_line()?;
93 write!(self.writer, "</")?;
94
95 self.writer.set_style(&Style("fg: blue; weight: bold"))?;
96 write!(self.writer, "{}", popped)?;
97 self.writer.reset()?;
98 write!(self.writer, ">")?;
99 self.line_start = true;
100
101 Ok(())
102 }
103
104 fn write_newline(&mut self) -> io::Result<()> {
105 let writer = &mut self.writer;
106 writer.reset()?;
107
108 if self.line_start {
109 write!(writer, "\n{}", PadItem(" ", self.nesting.len()))?;
110 }
111
112 write!(writer, "\\n",)?;
113 self.line_start = true;
114
115 Ok(())
116 }
117
118 fn start_line(&mut self) -> io::Result<()> {
119 let pad = self.pad();
120 write!(self.writer, "\n{}", pad)
121 }
122
123 fn styled_write(
124 &mut self,
125 value: impl fmt::Display,
126 style: impl Into<Style>,
127 ) -> io::Result<()> {
128 self.writer.set_style(style)?;
129 self.write(value)?;
130 self.writer.reset()
131 }
132
133 fn write(&mut self, value: impl fmt::Display) -> io::Result<()> {
134 write!(self.writer, "{}", value)
135 }
136
137 fn pad(&self) -> PadItem<&'static str> {
138 PadItem(" ", self.nesting.len())
139 }
140}
141
142impl Document {
143 pub fn debug_write(
144 &self,
145 writer: &mut impl WriteColor,
146 stylesheet: &Stylesheet,
147 ) -> io::Result<()> {
148 DebugDocument {
149 document: self,
150 writer,
151 stylesheet,
152 line_start: true,
153 nesting: vec![],
154 }.write_document()
155 }
156}