1use std::fmt;
7
8pub trait TreeDisplayContext<N: ?Sized> {
13 fn push_parent(&mut self, parent: &N) {
15 _ = parent;
16 }
17
18 fn pop_parent(&mut self, parent: &N) {
20 _ = parent;
21 }
22}
23
24impl<N: ?Sized> TreeDisplayContext<N> for () {}
25
26#[derive(Debug, Default)]
28pub struct DepthContext {
29 depth: usize,
30}
31
32impl DepthContext {
33 pub fn depth(&self) -> usize {
35 self.depth
36 }
37}
38
39impl<N: ?Sized> TreeDisplayContext<N> for DepthContext {
40 fn push_parent(&mut self, _parent: &N) {
41 self.depth += 1;
42 }
43
44 fn pop_parent(&mut self, _parent: &N) {
45 debug_assert!(self.depth > 0, "tree depth push/pop mismatch");
46 self.depth -= 1;
47 }
48}
49
50pub struct IndentedFormatter<'a, 'b> {
52 inner: &'a mut fmt::Formatter<'b>,
53 indent: &'a str,
54}
55
56impl<'a, 'b> IndentedFormatter<'a, 'b> {
57 fn new(inner: &'a mut fmt::Formatter<'b>, indent: &'a str) -> Self {
58 Self { inner, indent }
59 }
60
61 pub fn parts(&mut self) -> (&str, &mut fmt::Formatter<'b>) {
63 (self.indent, self.inner)
64 }
65
66 pub fn indent(&self) -> &str {
68 self.indent
69 }
70
71 pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> {
73 self.inner
74 }
75}
76
77pub trait TreeDisplayExtractor<N: ?Sized, C: TreeDisplayContext<N>>: Send + Sync {
79 fn write_header(
81 &self,
82 node: &N,
83 context: &C,
84 formatter: &mut fmt::Formatter<'_>,
85 ) -> fmt::Result {
86 _ = (node, context, formatter);
87 Ok(())
88 }
89
90 fn write_details(
92 &self,
93 node: &N,
94 context: &C,
95 formatter: &mut IndentedFormatter<'_, '_>,
96 ) -> fmt::Result {
97 _ = (node, context, formatter);
98 Ok(())
99 }
100}
101
102pub trait TreeDisplayAdapter {
104 type Node: ?Sized;
106
107 type Context: TreeDisplayContext<Self::Node>;
109
110 fn write_node(
115 &self,
116 node: &Self::Node,
117 context: &Self::Context,
118 formatter: &mut fmt::Formatter<'_>,
119 ) -> fmt::Result;
120
121 fn write_details(
123 &self,
124 node: &Self::Node,
125 context: &Self::Context,
126 formatter: &mut IndentedFormatter<'_, '_>,
127 ) -> fmt::Result {
128 _ = (node, context, formatter);
129 Ok(())
130 }
131
132 fn visit_children(
137 &self,
138 node: &Self::Node,
139 visit: &mut dyn FnMut(&str, &Self::Node, bool) -> fmt::Result,
140 ) -> fmt::Result;
141}
142
143pub fn write_indented_tree<A: TreeDisplayAdapter>(
148 adapter: &A,
149 root_name: &str,
150 root: &A::Node,
151 context: &mut A::Context,
152 formatter: &mut fmt::Formatter<'_>,
153) -> fmt::Result {
154 write_indented_node(adapter, root_name, root, context, "", formatter)
155}
156
157fn write_indented_node<A: TreeDisplayAdapter>(
158 adapter: &A,
159 name: &str,
160 node: &A::Node,
161 context: &mut A::Context,
162 indent: &str,
163 formatter: &mut fmt::Formatter<'_>,
164) -> fmt::Result {
165 write!(formatter, "{indent}{name}:")?;
166 adapter.write_node(node, context, formatter)?;
167 writeln!(formatter)?;
168
169 let child_indent = format!("{indent} ");
170 {
171 let mut indented = IndentedFormatter::new(formatter, &child_indent);
172 adapter.write_details(node, context, &mut indented)?;
173 }
174
175 context.push_parent(node);
176 let result = adapter.visit_children(node, &mut |child_name, child, _is_last| {
177 write_indented_node(
178 adapter,
179 child_name,
180 child,
181 context,
182 &child_indent,
183 formatter,
184 )
185 });
186 context.pop_parent(node);
187 result
188}
189
190pub fn write_branch_tree<A: TreeDisplayAdapter>(
195 adapter: &A,
196 root: &A::Node,
197 context: &mut A::Context,
198 formatter: &mut fmt::Formatter<'_>,
199) -> fmt::Result {
200 write_branch_node(adapter, root, context, "", formatter)
201}
202
203fn write_branch_node<A: TreeDisplayAdapter>(
204 adapter: &A,
205 node: &A::Node,
206 context: &mut A::Context,
207 prefix: &str,
208 formatter: &mut fmt::Formatter<'_>,
209) -> fmt::Result {
210 adapter.write_node(node, context, formatter)?;
211
212 context.push_parent(node);
213 let result = adapter.visit_children(node, &mut |child_name, child, is_last| {
214 writeln!(formatter)?;
215 let connector = if is_last { "└── " } else { "├── " };
216 write!(formatter, "{prefix}{connector}{child_name}: ")?;
217 let child_prefix = format!("{prefix}{}", if is_last { " " } else { "│ " });
218 write_branch_node(adapter, child, context, &child_prefix, formatter)
219 });
220 context.pop_parent(node);
221 result
222}
223
224#[cfg(test)]
225mod tests {
226 use std::fmt;
227
228 use super::DepthContext;
229 use super::TreeDisplayAdapter;
230 use super::write_branch_tree;
231 use super::write_indented_tree;
232
233 struct TestNode {
234 label: &'static str,
235 children: Vec<(&'static str, TestNode)>,
236 }
237
238 struct TestAdapter;
239
240 impl TreeDisplayAdapter for TestAdapter {
241 type Context = DepthContext;
242 type Node = TestNode;
243
244 fn write_node(
245 &self,
246 node: &Self::Node,
247 context: &Self::Context,
248 formatter: &mut fmt::Formatter<'_>,
249 ) -> fmt::Result {
250 write!(formatter, "{}@{}", node.label, context.depth())
251 }
252
253 fn visit_children(
254 &self,
255 node: &Self::Node,
256 visit: &mut dyn FnMut(&str, &Self::Node, bool) -> fmt::Result,
257 ) -> fmt::Result {
258 for (index, (name, child)) in node.children.iter().enumerate() {
259 visit(name, child, index + 1 == node.children.len())?;
260 }
261 Ok(())
262 }
263 }
264
265 struct IndentedDisplay<'a>(&'a TestNode);
266
267 impl fmt::Display for IndentedDisplay<'_> {
268 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
269 write_indented_tree(
270 &TestAdapter,
271 "root",
272 self.0,
273 &mut DepthContext::default(),
274 formatter,
275 )
276 }
277 }
278
279 struct BranchDisplay<'a>(&'a TestNode);
280
281 impl fmt::Display for BranchDisplay<'_> {
282 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
283 write_branch_tree(
284 &TestAdapter,
285 self.0,
286 &mut DepthContext::default(),
287 formatter,
288 )
289 }
290 }
291
292 fn tree() -> TestNode {
293 TestNode {
294 label: "parent",
295 children: vec![
296 (
297 "left",
298 TestNode {
299 label: "branch",
300 children: vec![(
301 "leaf",
302 TestNode {
303 label: "first",
304 children: Vec::new(),
305 },
306 )],
307 },
308 ),
309 (
310 "right",
311 TestNode {
312 label: "second",
313 children: Vec::new(),
314 },
315 ),
316 ],
317 }
318 }
319
320 #[test]
321 fn renders_indented_tree() {
322 assert_eq!(
323 IndentedDisplay(&tree()).to_string(),
324 "root:parent@0\n left:branch@1\n leaf:first@2\n right:second@1\n"
325 );
326 }
327
328 #[test]
329 fn renders_branch_tree() {
330 assert_eq!(
331 BranchDisplay(&tree()).to_string(),
332 "parent@0\n├── left: branch@1\n│ └── leaf: first@2\n└── right: second@1"
333 );
334 }
335}