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> { |
21 | pub fn new(root: D) -> Self { |
22 | Tree { |
23 | root, |
24 | leaves: Vec::new(), |
25 | multiline: false, |
26 | glyphs: GlyphPalette::new(), |
27 | } |
28 | } |
29 | |
30 | 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 |
36 | pub fn with_multiline(mut self, yes: bool) -> Self { |
37 | self.multiline = yes; |
38 | self |
39 | } |
40 | |
41 | /// Customize the rendering of this node |
42 | 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 |
50 | 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 |
56 | 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> { |
63 | 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> { |
70 | fn from(inner: D) -> Self { |
71 | Self::new(inner) |
72 | } |
73 | } |
74 | |
75 | impl<D: Display> Extend<D> for Tree<D> { |
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> { |
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> { |
88 | 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 | |
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 { |
193 | 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 { |
207 | fn default() -> Self { |
208 | Self::new() |
209 | } |
210 | } |
211 | |