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