README.md
1# Debug Tree
2
3This library allows you to build a tree one element at a time and output it as a pretty string.
4
5The tree can easily be output to a `String`, `stdout` or a file.
6
7This is particularly convenient for generating clean output from nested and recursive functions.
8
9* [Recursive Fibonacci Example](#recursive-fibonacci-example)
10* [Overview](#overview)
11* [More Examples](#more-examples)
12 * [Multiple Tagged Trees](#multiple-tagged-trees)
13 * [Nested Functions](#nested-functions)
14 * [Panic](#panics)
15 * [Without Macros](#without-macros)
16
17
18## Recursive Fibonacci Example
19
20Using the `add_branch!()` macro at the start of the `factors()` function, you can generate an entire call tree, with minimal effort.
21
22<!--{ fibonacci.rs | code: rust }-->
23```rust
24use debug_tree::*;
25
26fn factors(x: usize) {
27 add_branch!("{}", x); // <~ THE MAGIC LINE
28 for i in 1..x {
29 if x % i == 0 {
30 factors(i);
31 }
32 }
33}
34
35fn main() {
36 // output to file at the end of this block
37 defer_write!("examples/out/fibonacci.txt");
38 add_branch!("A Fibonacci Tree");
39 factors(6);
40 add_leaf!("That's All Folks!");
41}
42```
43<!--{ end }-->
44
45<!--{ out/fibonacci.txt | code }-->
46```
47A Fibonacci Tree
48├╼ 6
49│ ├╼ 1
50│ ├╼ 2
51│ │ └╼ 1
52│ └╼ 3
53│ └╼ 1
54└╼ That's All Folks!
55```
56<!--{ end }-->
57
58## Overview
59
60- Add a branch
61 - `add_branch!("Hello, {}", "World")`
62 - The branch will exit at the end of the current block
63
64- Add a leaf
65 - `add_leaf!("I am a {}", "leaf")`
66 - Added to the current scoped branch
67
68- Print a tree, or write it to file at the end of a block
69 - `defer_print!()`
70 - `defer_write!("filename.txt")`
71 - The tree will be empty after these calls
72 - To prevent clearing, use `defer_peek_print!` and `defer_peek_write!`
73
74
75- Handle multiple trees using named trees
76 - `add_branch_to!("A", "I'm a branch on tree 'A'")`
77 - `add_leaf_to!("A", "I'm a leaf on tree 'A'")`
78 - `defer_print!("A")`
79 - `defer_write!("A", "filename.txt")`
80
81- Get a named tree
82 - `tree("TREE_NAME")`
83
84- Retrieve the pretty-string from a tree
85 - `tree("TREE_NAME").string()`
86
87
88- Usage across threads
89 - `default_tree()` is local to each thread
90 - Named trees are shared between threads
91
92## More Examples
93
94### Multiple Tagged Trees
95
96If you need multiple, separated trees you can use a name tag.
97
98<!--{ multiple_trees.rs | code: rust }-->
99```rust
100use debug_tree::*;
101
102fn populate(tree_name: &str, n_children: usize) {
103 add_branch_to!(tree_name, "{} TREE", tree_name);
104 for _ in 0..n_children {
105 populate(tree_name, n_children / 2);
106 }
107}
108fn main() {
109 // Override tree config (just for "B")
110 let b_tree = tree("B");
111 b_tree.set_config_override(
112 TreeConfig::new()
113 .indent(4)
114 .symbols(TreeSymbols::with_rounded().leaf("> ")),
115 );
116 defer_write!(b_tree, "examples/out/multiple_trees_B.txt");
117 defer_write!("A", "examples/out/multiple_trees_A.txt");
118
119 populate("A", 2);
120 populate("B", 3);
121}
122```
123<!--{ end }-->
124<!--{ out/multiple_trees_A.txt | code }-->
125```
126A TREE
127├╼ A TREE
128│ └╼ A TREE
129└╼ A TREE
130 └╼ A TREE
131```
132<!--{ end }-->
133<!--{ out/multiple_trees_B.txt | code }-->
134```
135B TREE
136├──> B TREE
137│ ╰──> B TREE
138├──> B TREE
139│ ╰──> B TREE
140╰──> B TREE
141 ╰──> B TREE
142```
143<!--{ end }-->
144
145### Nested Functions
146
147Branches also make nested function calls a lot easier to follow.
148
149<!--{ nested.rs | code: rust }-->
150```rust
151use debug_tree::*;
152fn a() {
153 add_branch!("a");
154 b();
155 c();
156}
157fn b() {
158 add_branch!("b");
159 c();
160}
161fn c() {
162 add_branch!("c");
163 add_leaf!("Nothing to see here");
164}
165
166fn main() {
167 defer_write!("examples/out/nested.txt");
168 a();
169}
170```
171<!--{ end }-->
172<!--{ out/nested.txt | code }-->
173```
174a
175├╼ b
176│ └╼ c
177│ └╼ Nothing to see here
178└╼ c
179 └╼ Nothing to see here
180```
181<!--{ end }-->
182
183### Line Breaks
184
185Newlines in multi-line strings are automatically indented.
186
187<!--{ multi_line.rs | code: rust }-->
188```rust
189use debug_tree::*;
190fn main() {
191 // output to file at the end of this block
192 defer_write!("examples/out/multi_line.txt");
193 add_branch!("1");
194 add_leaf!("1.1\nAnother line...\n... and one more line");
195 add_leaf!("1.2");
196}
197```
198<!--{ end }-->
199
200<!--{ out/multi_line.txt | code }-->
201```
2021
203├╼ 1.1
204│ Another line...
205│ ... and one more line
206└╼ 1.2
207```
208<!--{ end }-->
209
210### Panics
211Even if there is a panic, the tree is not lost!
212The `defer_` functions were introduced to allow the tree
213to be printed our written to file in the case of a `panic!` or early return.
214
215<!--{ panic.rs | code: rust }-->
216```rust
217use debug_tree::*;
218
219fn i_will_panic() {
220 add_branch!("Here are my last words");
221 add_leaf!("Stay calm, and try not to panic");
222 panic!("I told you so...")
223}
224
225fn main() {
226 // output to file at the end of this block
227 defer_write!("examples/out/panic.txt");
228 // print at the end of this block
229 {
230 add_branch!("By using the 'defer_' functions");
231 add_branch!("Output will still be generated");
232 add_branch!("Otherwise you might lose your valuable tree!");
233 }
234 add_branch!("Now for something crazy...");
235 i_will_panic();
236}
237```
238<!--{ end }-->
239
240<!--{ out/panic.txt | code }-->
241```
242By using the 'defer_' functions
243└╼ Output will still be generated
244 └╼ Otherwise you might lose your valuable tree!
245Now for something crazy...
246└╼ Here are my last words
247 └╼ Stay calm, and try not to panic
248```
249<!--{ end }-->
250
251
252### Without Macros
253
254If you prefer not using macros, you can construct `TreeBuilder`s manually.
255
256<!--{ no_macros.rs | code: rust }-->
257```rust
258use debug_tree::TreeBuilder;
259
260fn main() {
261 // Make a new tree.
262 let tree = TreeBuilder::new();
263
264 // Add a scoped branch. The next item added will belong to the branch.
265 let mut branch = tree.add_branch("1 Branch");
266
267 // Add a leaf to the current branch
268 tree.add_leaf("1.1 Child");
269
270 // Leave scope early
271 branch.release();
272 tree.add_leaf("2 Sibling");
273 // output to file
274 tree.write("examples/out/no_macros.txt").ok(); // Write and flush.
275}
276```
277<!--{ end }-->
278<!--{ out/no_macros.txt | code }-->
279```
2801 Branch
281└╼ 1.1 Child
2822 Sibling
283```
284<!--{ end }-->