1 #![allow(clippy::branches_sharing_code)]
2 
3 #[cfg(test)]
4 mod tests;
5 
6 use std::collections::VecDeque;
7 use std::fmt::{self, Display};
8 use std::rc::Rc;
9 
10 /// a simple recursive type which is able to render its
11 /// components in a tree-like format
12 #[derive(Debug, Clone)]
13 pub struct Tree<D: Display> {
14     pub root: D,
15     pub leaves: Vec<Tree<D>>,
16     multiline: bool,
17     glyphs: GlyphPalette,
18 }
19 
20 impl<D: Display> Tree<D> {
new(root: D) -> Self21     pub fn new(root: D) -> Self {
22         Tree {
23             root,
24             leaves: Vec::new(),
25             multiline: false,
26             glyphs: GlyphPalette::new(),
27         }
28     }
29 
with_leaves(mut self, leaves: impl IntoIterator<Item = impl Into<Tree<D>>>) -> Self30     pub fn with_leaves(mut self, leaves: impl IntoIterator<Item = impl Into<Tree<D>>>) -> Self {
31         self.leaves = leaves.into_iter().map(Into::into).collect();
32         self
33     }
34 
35     /// Ensure all lines for `root` are indented
with_multiline(mut self, yes: bool) -> Self36     pub fn with_multiline(mut self, yes: bool) -> Self {
37         self.multiline = yes;
38         self
39     }
40 
41     /// Customize the rendering of this node
with_glyphs(mut self, glyphs: GlyphPalette) -> Self42     pub fn with_glyphs(mut self, glyphs: GlyphPalette) -> Self {
43         self.glyphs = glyphs;
44         self
45     }
46 }
47 
48 impl<D: Display> Tree<D> {
49     /// Ensure all lines for `root` are indented
set_multiline(&mut self, yes: bool) -> &mut Self50     pub fn set_multiline(&mut self, yes: bool) -> &mut Self {
51         self.multiline = yes;
52         self
53     }
54 
55     /// Customize the rendering of this node
set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self56     pub fn set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self {
57         self.glyphs = glyphs;
58         self
59     }
60 }
61 
62 impl<D: Display> Tree<D> {
push(&mut self, leaf: impl Into<Tree<D>>) -> &mut Self63     pub fn push(&mut self, leaf: impl Into<Tree<D>>) -> &mut Self {
64         self.leaves.push(leaf.into());
65         self
66     }
67 }
68 
69 impl<D: Display> From<D> for Tree<D> {
from(inner: D) -> Self70     fn from(inner: D) -> Self {
71         Self::new(inner)
72     }
73 }
74 
75 impl<D: Display> Extend<D> for Tree<D> {
extend<T: IntoIterator<Item = D>>(&mut self, iter: T)76     fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) {
77         self.leaves.extend(iter.into_iter().map(Into::into))
78     }
79 }
80 
81 impl<D: Display> Extend<Tree<D>> for Tree<D> {
extend<T: IntoIterator<Item = Tree<D>>>(&mut self, iter: T)82     fn extend<T: IntoIterator<Item = Tree<D>>>(&mut self, iter: T) {
83         self.leaves.extend(iter)
84     }
85 }
86 
87 impl<D: Display> Display for Tree<D> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result88     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89         self.root.fmt(f)?; // Pass along `f.alternate()`
90         writeln!(f)?;
91         let mut queue = DisplauQueue::new();
92         let no_space = Rc::new(Vec::new());
93         enqueue_leaves(&mut queue, self, no_space);
94         while let Some((last, leaf, spaces)) = queue.pop_front() {
95             let mut prefix = (
96                 if last {
97                     leaf.glyphs.last_item
98                 } else {
99                     leaf.glyphs.middle_item
100                 },
101                 leaf.glyphs.item_indent,
102             );
103 
104             if leaf.multiline {
105                 let rest_prefix = (
106                     if last {
107                         leaf.glyphs.last_skip
108                     } else {
109                         leaf.glyphs.middle_skip
110                     },
111                     leaf.glyphs.skip_indent,
112                 );
113                 debug_assert_eq!(prefix.0.chars().count(), rest_prefix.0.chars().count());
114                 debug_assert_eq!(prefix.1.chars().count(), rest_prefix.1.chars().count());
115 
116                 let root = if f.alternate() {
117                     format!("{:#}", leaf.root)
118                 } else {
119                     format!("{:}", leaf.root)
120                 };
121                 for line in root.lines() {
122                     // print single line
123                     for s in spaces.as_slice() {
124                         if *s {
125                             self.glyphs.last_skip.fmt(f)?;
126                             self.glyphs.skip_indent.fmt(f)?;
127                         } else {
128                             self.glyphs.middle_skip.fmt(f)?;
129                             self.glyphs.skip_indent.fmt(f)?;
130                         }
131                     }
132                     prefix.0.fmt(f)?;
133                     prefix.1.fmt(f)?;
134                     line.fmt(f)?;
135                     writeln!(f)?;
136                     prefix = rest_prefix;
137                 }
138             } else {
139                 // print single line
140                 for s in spaces.as_slice() {
141                     if *s {
142                         self.glyphs.last_skip.fmt(f)?;
143                         self.glyphs.skip_indent.fmt(f)?;
144                     } else {
145                         self.glyphs.middle_skip.fmt(f)?;
146                         self.glyphs.skip_indent.fmt(f)?;
147                     }
148                 }
149                 prefix.0.fmt(f)?;
150                 prefix.1.fmt(f)?;
151                 leaf.root.fmt(f)?; // Pass along `f.alternate()`
152                 writeln!(f)?;
153             }
154 
155             // recurse
156             if !leaf.leaves.is_empty() {
157                 let s: &Vec<bool> = &spaces;
158                 let mut child_spaces = s.clone();
159                 child_spaces.push(last);
160                 let child_spaces = Rc::new(child_spaces);
161                 enqueue_leaves(&mut queue, leaf, child_spaces);
162             }
163         }
164         Ok(())
165     }
166 }
167 
168 type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree<D>, Rc<Vec<bool>>)>;
169 
enqueue_leaves<'t, D: Display>( queue: &mut DisplauQueue<'t, D>, parent: &'t Tree<D>, spaces: Rc<Vec<bool>>, )170 fn enqueue_leaves<'t, D: Display>(
171     queue: &mut DisplauQueue<'t, D>,
172     parent: &'t Tree<D>,
173     spaces: Rc<Vec<bool>>,
174 ) {
175     for (i, leaf) in parent.leaves.iter().rev().enumerate() {
176         let last = i == 0;
177         queue.push_front((last, leaf, spaces.clone()));
178     }
179 }
180 
181 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
182 pub struct GlyphPalette {
183     pub middle_item: &'static str,
184     pub last_item: &'static str,
185     pub item_indent: &'static str,
186 
187     pub middle_skip: &'static str,
188     pub last_skip: &'static str,
189     pub skip_indent: &'static str,
190 }
191 
192 impl GlyphPalette {
new() -> Self193     pub const fn new() -> Self {
194         Self {
195             middle_item: "├",
196             last_item: "└",
197             item_indent: "── ",
198 
199             middle_skip: "│",
200             last_skip: " ",
201             skip_indent: "   ",
202         }
203     }
204 }
205 
206 impl Default for GlyphPalette {
default() -> Self207     fn default() -> Self {
208         Self::new()
209     }
210 }
211