1use core::fmt::{self, Write as _};
4
5#[cfg(not(feature = "std"))]
6use alloc::vec::Vec;
7#[cfg(feature = "std")]
8use std::vec::Vec;
9
10use crate::{
11 arena::Arena,
12 id::NodeId,
13 traverse::{NodeEdge, Traverse},
14};
15
16#[derive(Clone, Copy)]
21struct IndentedBlockState {
22 is_last_item: bool,
24 is_first_line: bool,
26}
27
28impl IndentedBlockState {
29 #[inline]
31 fn as_str(self) -> &'static str {
32 match (self.is_last_item, self.is_first_line) {
33 (false, true) => "|-- ",
34 (false, false) => "| ",
35 (true, true) => "`-- ",
36 (true, false) => " ",
37 }
38 }
39
40 #[inline]
42 fn as_str_leading(self) -> &'static str {
43 match (self.is_last_item, self.is_first_line) {
44 (false, true) => "|--",
45 (false, false) => "|",
46 (true, true) => "`--",
47 (true, false) => "",
48 }
49 }
50
51 #[inline]
53 fn as_str_trailing_spaces(self) -> &'static str {
54 match (self.is_last_item, self.is_first_line) {
55 (_, true) => " ",
56 (false, false) => " ",
57 (true, false) => " ",
58 }
59 }
60
61 #[inline]
63 #[must_use]
64 fn is_all_whitespace(self) -> bool {
65 self.is_last_item && !self.is_first_line
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71enum LineState {
72 BeforeIndent,
74 PartialIndent,
78 Content,
80}
81
82struct IndentWriter<'a, 'b> {
84 fmt: &'b mut fmt::Formatter<'a>,
86 line_state: LineState,
88 indents: Vec<IndentedBlockState>,
90 pending_ws_only_indent_level: usize,
92}
93
94impl<'a, 'b> IndentWriter<'a, 'b> {
95 #[inline]
97 fn new(fmt: &'b mut fmt::Formatter<'a>) -> Self {
98 Self {
99 fmt,
100 line_state: LineState::BeforeIndent,
101 indents: Vec::new(),
102 pending_ws_only_indent_level: 0,
103 }
104 }
105
106 fn open_item(&mut self, is_last_item: bool) -> fmt::Result {
112 if self.line_state != LineState::BeforeIndent {
113 self.fmt.write_char('\n')?;
114 self.line_state = LineState::BeforeIndent;
115 self.pending_ws_only_indent_level = 0;
116 }
117 if let Some(indent) = self.indents.last_mut() {
118 indent.is_first_line = false;
119 }
120 self.indents.push(IndentedBlockState {
121 is_last_item,
122 is_first_line: true,
123 });
124
125 Ok(())
126 }
127
128 #[inline]
134 fn close_item(&mut self) -> Result<(), ()> {
135 match self.indents.pop() {
136 Some(_) => Ok(()),
137 None => Err(()),
138 }
139 }
140
141 fn write_indent_partial(&mut self) -> fmt::Result {
143 self.pending_ws_only_indent_level = self
144 .indents
145 .iter()
146 .rev()
147 .take_while(|i| i.is_all_whitespace())
148 .count();
149
150 let ws_indent_first_level = self.indents.len() - self.pending_ws_only_indent_level;
151 let indents_to_print = &self.indents[..ws_indent_first_level];
152 if let Some(last) = indents_to_print.last() {
153 for indent in &indents_to_print[..(indents_to_print.len() - 1)] {
154 self.fmt.write_str(indent.as_str())?;
155 }
156 self.fmt.write_str(last.as_str_leading())?;
157 }
158
159 Ok(())
160 }
161
162 fn complete_partial_indent(&mut self) -> fmt::Result {
164 debug_assert_eq!(self.line_state, LineState::PartialIndent);
165 if let Some(last_non_ws_indent_level) =
166 (self.indents.len() - self.pending_ws_only_indent_level).checked_sub(1)
167 {
168 self.fmt
169 .write_str(self.indents[last_non_ws_indent_level].as_str_trailing_spaces())?;
170 }
171 for _ in 0..self.pending_ws_only_indent_level {
172 self.fmt.write_str(" ")?;
173 }
174 self.pending_ws_only_indent_level = 0;
175
176 Ok(())
177 }
178}
179
180impl fmt::Write for IndentWriter<'_, '_> {
181 fn write_str(&mut self, mut s: &str) -> fmt::Result {
182 while !s.is_empty() {
183 if self.line_state == LineState::BeforeIndent {
186 self.write_indent_partial()?;
187 self.line_state = LineState::PartialIndent;
188 }
189
190 let (line_end, ends_with_newline) = match s.find('\n') {
191 Some(pos) => (pos + 1, true),
192 None => (s.len(), false),
193 };
194 let content = &s[..line_end];
195 if !content.is_empty() {
196 debug_assert_ne!(
197 self.line_state,
198 LineState::BeforeIndent,
199 "[consistency] indent must be written since there are something to write"
200 );
201 if self.line_state == LineState::PartialIndent {
202 self.complete_partial_indent()?;
203 }
204 if let Some(level) = self.indents.last_mut() {
205 level.is_first_line = level.is_first_line && !ends_with_newline;
206 }
207 self.fmt.write_str(content)?;
208
209 self.line_state = if ends_with_newline {
210 LineState::BeforeIndent
211 } else {
212 LineState::Content
213 };
214 }
215 s = &s[line_end..];
216 }
217
218 Ok(())
219 }
220}
221
222#[derive(Clone, Copy)]
231pub struct DebugPrettyPrint<'a, T> {
232 id: &'a NodeId,
234 arena: &'a Arena<T>,
236}
237
238impl<'a, T> DebugPrettyPrint<'a, T> {
239 #[inline]
241 pub(crate) fn new(id: &'a NodeId, arena: &'a Arena<T>) -> Self {
242 Self { id, arena }
243 }
244}
245
246impl<T: fmt::Display> fmt::Display for DebugPrettyPrint<'_, T> {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 let is_alternate = f.alternate();
249 let mut writer = IndentWriter::new(f);
250 let mut traverser = self.id.traverse(self.arena);
251
252 traverser.next();
254 {
255 let data = self.arena[*self.id].get();
256 if is_alternate {
257 write!(writer, "{:#}", data)?
258 } else {
259 write!(writer, "{}", data)?
260 }
261 }
262
263 while let Some(id) = prepare_next_node_printing(&mut writer, &mut traverser)? {
265 let data = traverser.arena()[id].get();
266 if is_alternate {
267 write!(writer, "{:#}", data)?
268 } else {
269 write!(writer, "{}", data)?
270 }
271 }
272
273 Ok(())
274 }
275}
276
277impl<T: fmt::Debug> fmt::Debug for DebugPrettyPrint<'_, T> {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 let is_alternate = f.alternate();
280 let mut writer = IndentWriter::new(f);
281 let mut traverser = self.id.traverse(self.arena);
282
283 traverser.next();
285 {
286 let data = self.arena[*self.id].get();
287 if is_alternate {
288 write!(writer, "{:#?}", data)?
289 } else {
290 write!(writer, "{:?}", data)?
291 }
292 }
293
294 while let Some(id) = prepare_next_node_printing(&mut writer, &mut traverser)? {
296 let data = traverser.arena()[id].get();
297 if is_alternate {
298 write!(writer, "{:#?}", data)?
299 } else {
300 write!(writer, "{:?}", data)?
301 }
302 }
303
304 Ok(())
305 }
306}
307
308fn prepare_next_node_printing<T>(
312 writer: &mut IndentWriter<'_, '_>,
313 traverser: &mut Traverse<'_, T>,
314) -> Result<Option<NodeId>, fmt::Error> {
315 while let Some(ev) = traverser.next() {
318 let id = match ev {
319 NodeEdge::Start(id) => id,
320 NodeEdge::End(_) => {
321 if writer.close_item().is_ok() {
322 continue;
324 } else {
325 break;
327 }
328 }
329 };
330 let is_last_sibling = traverser.arena()[id].next_sibling().is_none();
331 writer.open_item(is_last_sibling)?;
332
333 return Ok(Some(id));
334 }
335
336 Ok(None)
337}