indextree/
debug_pretty_print.rs

1//! Debug printer.
2
3use 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//use crate::dynamic::hierarchy::traverse::{DepthFirstTraverser, DftEvent};
17//use crate::dynamic::hierarchy::Hierarchy;
18
19/// State of an indent block.
20#[derive(Clone, Copy)]
21struct IndentedBlockState {
22    /// Whether this is the last item.
23    is_last_item: bool,
24    /// Whether the line is the first line.
25    is_first_line: bool,
26}
27
28impl IndentedBlockState {
29    /// Returns the indent string for the indent type.
30    #[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    /// Returns the leading part of the indent string.
41    #[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    /// Returns the trailing whitespaces part of the indent string.
52    #[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    /// Returns whether the indent string consists of only whitespaces.
62    #[inline]
63    #[must_use]
64    fn is_all_whitespace(self) -> bool {
65        self.is_last_item && !self.is_first_line
66    }
67}
68
69/// State of the line writing.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71enum LineState {
72    /// Before any character of the indent is written to the current line.
73    BeforeIndent,
74    /// Indents are partially written.
75    ///
76    /// More precisely, trailing whitespaces are not yet written.
77    PartialIndent,
78    /// Writing content.
79    Content,
80}
81
82/// Indent writer for the debug printer.
83struct IndentWriter<'a, 'b> {
84    /// Backend formatter.
85    fmt: &'b mut fmt::Formatter<'a>,
86    /// State of the line writing.
87    line_state: LineState,
88    /// Indents.
89    indents: Vec<IndentedBlockState>,
90    /// The number of pending whitespace-only indents.
91    pending_ws_only_indent_level: usize,
92}
93
94impl<'a, 'b> IndentWriter<'a, 'b> {
95    /// Creates a new `PadAdapter`.
96    #[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    /// Opens the next item.
107    ///
108    /// Writes a newline if necessary and prepares to write the next item.
109    ///
110    /// This should **not** be called for the root item.
111    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    /// Closes the current item.
129    ///
130    /// Returns `Ok(())` if an item is successfully closed.
131    /// Returns `Err(())` if there are no items that can be closed, i.e. the
132    /// current item is the root.
133    #[inline]
134    fn close_item(&mut self) -> Result<(), ()> {
135        match self.indents.pop() {
136            Some(_) => Ok(()),
137            None => Err(()),
138        }
139    }
140
141    /// Writes the indent except for the trailing whitespaces.
142    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    /// Writes the rest of the indents which are partially written.
163    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            // There remains something to print.
184
185            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/// Tree printer for debugging.
223///
224/// This is provided mainly for debugging purpose. Node that the output format
225/// is not guaranteed to be stable, and any format changes won't be considered
226/// as breaking changes.
227///
228/// For usage and output examples, see
229/// [`NodeId::debug_pretty_print`][`crate::NodeId::debug_pretty_print`] method.
230#[derive(Clone, Copy)]
231pub struct DebugPrettyPrint<'a, T> {
232    /// Root node ID of the (sub)tree to print.
233    id: &'a NodeId,
234    /// Arena the node belongs to.
235    arena: &'a Arena<T>,
236}
237
238impl<'a, T> DebugPrettyPrint<'a, T> {
239    /// Creates a new `DebugPrettyPrint` object for the node.
240    #[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        // Print the first (root) node.
253        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        // Print the descendants.
264        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        // Print the first (root) node.
284        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        // Print the descendants.
295        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
308/// Prepares printing of next node.
309///
310/// Internally, this searches next node open and adjust indent level and prefix.
311fn prepare_next_node_printing<T>(
312    writer: &mut IndentWriter<'_, '_>,
313    traverser: &mut Traverse<'_, T>,
314) -> Result<Option<NodeId>, fmt::Error> {
315    // Not using `for ev in traverser` in order to access to `traverser`
316    // directly in the loop.
317    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                    // Closed a non-root node.
323                    continue;
324                } else {
325                    // Closed the root node.
326                    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}