1 use std::collections::VecDeque;
2 use std::fmt::{self, Display};
3 use std::rc::Rc;
4 
5 /// a simple recursive type which is able to render its
6 /// components in a tree-like format
7 #[derive(Debug)]
8 pub struct Tree<D: Display> {
9     root: D,
10     leaves: Vec<Tree<D>>,
11     multiline: bool,
12     glyphs: GlyphPalette,
13 }
14 
15 impl<D: Display> Tree<D> {
new(root: D, leaves: Vec<Tree<D>>) -> Tree<D>16     pub fn new(root: D, leaves: Vec<Tree<D>>) -> Tree<D> {
17         Tree {
18             root,
19             leaves,
20             multiline: false,
21             glyphs: GlyphPalette::new(),
22         }
23     }
24 
root(root: D) -> Tree<D>25     pub fn root(root: D) -> Tree<D> {
26         Tree {
27             root,
28             leaves: Vec::new(),
29             multiline: false,
30             glyphs: GlyphPalette::new(),
31         }
32     }
33 
34     /// Ensure all lines for `root` are indented
with_multiline(mut self, yes: bool) -> Self35     pub fn with_multiline(mut self, yes: bool) -> Self {
36         self.multiline = yes;
37         self
38     }
39 
40     /// Ensure all lines for `root` are indented
set_multiline(&mut self, yes: bool) -> &mut Self41     pub fn set_multiline(&mut self, yes: bool) -> &mut Self {
42         self.multiline = yes;
43         self
44     }
45 
46     /// Customize the rendering of this node
with_glyphs(mut self, glyphs: GlyphPalette) -> Self47     pub fn with_glyphs(mut self, glyphs: GlyphPalette) -> Self {
48         self.glyphs = glyphs;
49         self
50     }
51 
52     /// Customize the rendering of this node
set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self53     pub fn set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self {
54         self.glyphs = glyphs;
55         self
56     }
57 
push(&mut self, leaf: Tree<D>) -> &mut Self58     pub fn push(&mut self, leaf: Tree<D>) -> &mut Self {
59         self.leaves.push(leaf);
60         self
61     }
62 }
63 
64 impl<D: Display> Extend<D> for Tree<D> {
extend<T: IntoIterator<Item = D>>(&mut self, iter: T)65     fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) {
66         self.leaves.extend(iter.into_iter().map(Tree::root))
67     }
68 }
69 
70 impl<D: Display> Extend<Tree<D>> for Tree<D> {
extend<T: IntoIterator<Item = Tree<D>>>(&mut self, iter: T)71     fn extend<T: IntoIterator<Item = Tree<D>>>(&mut self, iter: T) {
72         self.leaves.extend(iter)
73     }
74 }
75 
76 impl<D: Display> Display for Tree<D> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result77     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78         writeln!(f, "{}", self.root)?;
79         let mut queue = DisplauQueue::new();
80         let no_space = Rc::new(Vec::new());
81         enqueue_leaves(&mut queue, self, no_space);
82         while let Some((last, leaf, spaces)) = queue.pop_front() {
83             let mut prefix = (
84                 if last {
85                     leaf.glyphs.last_item
86                 } else {
87                     leaf.glyphs.middle_item
88                 },
89                 leaf.glyphs.item_indent,
90             );
91 
92             if leaf.multiline {
93                 let rest_prefix = (
94                     if last {
95                         leaf.glyphs.last_skip
96                     } else {
97                         leaf.glyphs.middle_skip
98                     },
99                     leaf.glyphs.skip_indent,
100                 );
101                 debug_assert_eq!(prefix.0.chars().count(), rest_prefix.0.chars().count());
102                 debug_assert_eq!(prefix.1.chars().count(), rest_prefix.1.chars().count());
103 
104                 let root = leaf.root.to_string();
105                 for line in root.lines() {
106                     // print single line
107                     for s in spaces.as_slice() {
108                         if *s {
109                             write!(f, "{}{}", self.glyphs.last_skip, self.glyphs.skip_indent)?;
110                         } else {
111                             write!(f, "{}{}", self.glyphs.middle_skip, self.glyphs.skip_indent)?;
112                         }
113                     }
114                     writeln!(f, "{}{}{}", prefix.0, prefix.1, line)?;
115                     prefix = rest_prefix;
116                 }
117             } else {
118                 // print single line
119                 for s in spaces.as_slice() {
120                     if *s {
121                         write!(f, "{}{}", self.glyphs.last_skip, self.glyphs.skip_indent)?;
122                     } else {
123                         write!(f, "{}{}", self.glyphs.middle_skip, self.glyphs.skip_indent)?;
124                     }
125                 }
126                 writeln!(f, "{}{}{}", prefix.0, prefix.1, leaf.root)?;
127             }
128 
129             // recurse
130             if !leaf.leaves.is_empty() {
131                 let s: &Vec<bool> = &spaces;
132                 let mut child_spaces = s.clone();
133                 child_spaces.push(last);
134                 let child_spaces = Rc::new(child_spaces);
135                 enqueue_leaves(&mut queue, leaf, child_spaces);
136             }
137         }
138         Ok(())
139     }
140 }
141 
142 type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree<D>, Rc<Vec<bool>>)>;
143 
enqueue_leaves<'t, D: Display>( queue: &mut DisplauQueue<'t, D>, parent: &'t Tree<D>, spaces: Rc<Vec<bool>>, )144 fn enqueue_leaves<'t, D: Display>(
145     queue: &mut DisplauQueue<'t, D>,
146     parent: &'t Tree<D>,
147     spaces: Rc<Vec<bool>>,
148 ) {
149     for (i, leaf) in parent.leaves.iter().rev().enumerate() {
150         let last = i == 0;
151         queue.push_front((last, leaf, spaces.clone()));
152     }
153 }
154 
155 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
156 pub struct GlyphPalette {
157     pub middle_item: &'static str,
158     pub last_item: &'static str,
159     pub item_indent: &'static str,
160 
161     pub middle_skip: &'static str,
162     pub last_skip: &'static str,
163     pub skip_indent: &'static str,
164 }
165 
166 impl GlyphPalette {
new() -> Self167     pub const fn new() -> Self {
168         Self {
169             middle_item: "├",
170             last_item: "└",
171             item_indent: "── ",
172 
173             middle_skip: "│",
174             last_skip: " ",
175             skip_indent: "   ",
176         }
177     }
178 }
179 
180 impl Default for GlyphPalette {
default() -> Self181     fn default() -> Self {
182         Self::new()
183     }
184 }
185 
186 #[cfg(test)]
187 mod tests {
188     use super::Tree;
189     #[test]
render_tree_root()190     fn render_tree_root() {
191         let tree = Tree::root("foo");
192         assert_eq!(format!("{}", tree), "foo\n")
193     }
194 
195     #[test]
render_tree_with_leaves()196     fn render_tree_with_leaves() {
197         let tree = Tree::new("foo", vec![Tree::new("bar", vec![Tree::root("baz")])]);
198         assert_eq!(
199             format!("{}", tree),
200             r#"foo
201 └── bar
202     └── baz
203 "#
204         )
205     }
206 
207     #[test]
render_tree_with_multiple_leaves()208     fn render_tree_with_multiple_leaves() {
209         let tree = Tree::new("foo", vec![Tree::root("bar"), Tree::root("baz")]);
210         assert_eq!(
211             format!("{}", tree),
212             r#"foo
213 ├── bar
214 └── baz
215 "#
216         )
217     }
218 
219     #[test]
render_tree_with_multiline_leaf()220     fn render_tree_with_multiline_leaf() {
221         let tree = Tree::new(
222             "foo",
223             vec![
224                 Tree::root("hello\nworld").with_multiline(true),
225                 Tree::root("goodbye\nworld").with_multiline(true),
226             ],
227         );
228         assert_eq!(
229             format!("{}", tree),
230             r#"foo
231 ├── hello
232 │   world
233 └── goodbye
234     world
235 "#
236         )
237     }
238 }
239