1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use std::io::Write;
6 use std::fmt::Write as FmtWrite;
7 
8 /// A struct that makes it easier to print out a pretty tree of data, which
9 /// can be visually scanned more easily.
10 pub struct PrintTree<W>
11 where
12     W: Write
13 {
14     /// The current level of recursion.
15     level: u32,
16 
17     /// An item which is queued up, so that we can determine if we need
18     /// a mid-tree prefix or a branch ending prefix.
19     queued_item: Option<String>,
20 
21     // We hold lines until they are done, and then output them all at
22     // once
23     line_buffer: String,
24 
25     /// The sink to print to.
26     sink: W,
27 }
28 
29 /// A trait that makes it easy to describe a pretty tree of data,
30 /// regardless of the printing destination, to either print it
31 /// directly to stdout, or serialize it as in the debugger
32 pub trait PrintTreePrinter {
new_level(&mut self, title: String)33     fn new_level(&mut self, title: String);
end_level(&mut self)34     fn end_level(&mut self);
add_item(&mut self, text: String)35     fn add_item(&mut self, text: String);
36 }
37 
38 // The default does nothing but log
39 impl PrintTree<std::io::Sink> {
new(title: &str) -> Self40     pub fn new(title: &str) -> Self {
41         PrintTree::new_with_sink(title, std::io::sink())
42     }
43 }
44 
45 impl<W> PrintTree<W>
46 where
47     W: Write
48 {
new_with_sink(title: &str, sink: W) -> Self49     pub fn new_with_sink(title: &str, sink: W) -> Self {
50         let mut result = PrintTree {
51             level: 1,
52             queued_item: None,
53             line_buffer: String::new(),
54             sink,
55         };
56 
57         writeln!(result.line_buffer, "\u{250c} {}", title).unwrap();
58         result.flush_line();
59         result
60     }
61 
print_level_prefix(&mut self)62     fn print_level_prefix(&mut self) {
63         for _ in 0 .. self.level {
64             write!(self.line_buffer, "\u{2502}  ").unwrap();
65         }
66     }
67 
flush_queued_item(&mut self, prefix: &str)68     fn flush_queued_item(&mut self, prefix: &str) {
69         if let Some(queued_item) = self.queued_item.take() {
70             self.print_level_prefix();
71             writeln!(self.line_buffer, "{} {}", prefix, queued_item).unwrap();
72             self.flush_line();
73         }
74     }
75 
flush_line(&mut self)76     fn flush_line(&mut self) {
77         debug!("{}", self.line_buffer);
78         self.sink.write_all(self.line_buffer.as_bytes()).unwrap();
79         self.line_buffer.clear();
80     }
81 }
82 
83 impl<W> PrintTreePrinter for PrintTree<W>
84 where
85     W: Write
86 {
87     /// Descend one level in the tree with the given title.
new_level(&mut self, title: String)88     fn new_level(&mut self, title: String) {
89         self.flush_queued_item("\u{251C}\u{2500}");
90 
91         self.print_level_prefix();
92         writeln!(self.line_buffer, "\u{251C}\u{2500} {}", title).unwrap();
93         self.flush_line();
94 
95         self.level = self.level + 1;
96     }
97 
98     /// Ascend one level in the tree.
end_level(&mut self)99     fn end_level(&mut self) {
100         self.flush_queued_item("\u{2514}\u{2500}");
101         self.level = self.level - 1;
102     }
103 
104     /// Add an item to the current level in the tree.
add_item(&mut self, text: String)105     fn add_item(&mut self, text: String) {
106         self.flush_queued_item("\u{251C}\u{2500}");
107         self.queued_item = Some(text);
108     }
109 }
110 
111 impl<W> Drop for PrintTree<W>
112 where
113     W: Write
114 {
drop(&mut self)115     fn drop(&mut self) {
116         self.flush_queued_item("\u{9492}\u{9472}");
117     }
118 }
119 
120 pub trait PrintableTree {
print_with<T: PrintTreePrinter>(&self, pt: &mut T)121     fn print_with<T: PrintTreePrinter>(&self, pt: &mut T);
122 }
123