pub struct DAG<'a> { /* private fields */ }Expand description
A Directed Acyclic Graph (DAG) with ASCII rendering capabilities.
§Examples
use ascii_dag::graph::DAG;
let mut dag = DAG::new();
dag.add_node(1, "Start");
dag.add_node(2, "End");
dag.add_edge(1, 2);
let output = dag.render();
assert!(output.contains("Start"));
assert!(output.contains("End"));Implementations§
Source§impl<'a> DAG<'a>
impl<'a> DAG<'a>
Sourcepub fn has_cycle(&self) -> bool
pub fn has_cycle(&self) -> bool
Check if the graph contains cycles (making it not a valid DAG).
§Examples
use ascii_dag::graph::DAG;
let mut dag = DAG::new();
dag.add_node(1, "A");
dag.add_node(2, "B");
dag.add_edge(1, 2);
dag.add_edge(2, 1); // Creates a cycle!
assert!(dag.has_cycle());Examples found in repository?
3fn main() {
4 println!("=== Error Chain Visualization ===\n");
5
6 // Simulate an error dependency chain
7 // FileNotFound -> ParseError -> CompilationFailed -> BuildFailed
8
9 let mut dag = DAG::new();
10
11 dag.add_node(1, "FileNotFound");
12 dag.add_node(2, "ParseError");
13 dag.add_node(3, "CompilationFailed");
14 dag.add_node(4, "BuildFailed");
15
16 dag.add_edge(1, 2); // FileNotFound caused ParseError
17 dag.add_edge(2, 3); // ParseError caused CompilationFailed
18 dag.add_edge(3, 4); // CompilationFailed caused BuildFailed
19
20 println!("Error Dependency Chain:");
21 println!("{}", dag.render());
22
23 // More complex error scenario
24 println!("\n=== Complex Error Scenario ===\n");
25
26 let dag = DAG::from_edges(
27 &[
28 (1, "ConfigMissing"),
29 (2, "DBConnFail"),
30 (3, "AuthFail"),
31 (4, "InitError"),
32 (5, "StartupFail"),
33 ],
34 &[
35 (1, 2), // ConfigMissing -> DBConnFail
36 (1, 3), // ConfigMissing -> AuthFail
37 (2, 4), // DBConnFail -> InitError
38 (3, 4), // AuthFail -> InitError
39 (4, 5), // InitError -> StartupFail
40 ],
41 );
42
43 println!("Multiple Error Paths:");
44 println!("{}", dag.render());
45
46 // Check for cycles (should not have any)
47 if dag.has_cycle() {
48 println!("\n⚠️ WARNING: Circular error dependency detected!");
49 } else {
50 println!("\n✓ No circular dependencies");
51 }
52}Source§impl<'a> DAG<'a>
impl<'a> DAG<'a>
Sourcepub fn new() -> Self
pub fn new() -> Self
Examples found in repository?
3fn main() {
4 let mut dag = DAG::new();
5
6 println!("=== Test: Auto-created Node Promotion ===\n");
7
8 dag.add_node(1, "Start");
9 dag.add_edge(1, 2); // Auto-creates node 2
10
11 println!("Before promotion (node 2 is auto-created):");
12 println!("{}\n", dag.render());
13
14 // Now promote node 2
15 dag.add_node(2, "Promoted");
16
17 println!("After promotion (node 2 now has label 'Promoted'):");
18 println!("{}\n", dag.render());
19
20 println!("✓ Node 2 successfully promoted from ⟨2⟩ to [Promoted]");
21}More examples
3fn main() {
4 println!("=== Basic Usage Examples ===\n");
5
6 // Example 1: Simple chain
7 println!("1. Simple Chain (A -> B -> C):");
8 let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
9 println!("{}\n", dag.render());
10
11 // Example 2: Diamond pattern
12 println!("2. Diamond Pattern:");
13 let dag = DAG::from_edges(
14 &[(1, "Root"), (2, "Left"), (3, "Right"), (4, "Merge")],
15 &[(1, 2), (1, 3), (2, 4), (3, 4)],
16 );
17 println!("{}\n", dag.render());
18
19 // Example 3: Builder API
20 println!("3. Builder API:");
21 let mut dag = DAG::new();
22 dag.add_node(1, "Parse");
23 dag.add_node(2, "Compile");
24 dag.add_node(3, "Link");
25 dag.add_edge(1, 2);
26 dag.add_edge(2, 3);
27 println!("{}\n", dag.render());
28
29 // Example 4: Multi-convergence
30 println!("4. Multi-Convergence:");
31 let dag = DAG::from_edges(
32 &[(1, "E1"), (2, "E2"), (3, "E3"), (4, "Final")],
33 &[(1, 4), (2, 4), (3, 4)],
34 );
35 println!("{}\n", dag.render());
36}3fn main() {
4 // Create a graph where a level's children are clustered on opposite sides
5 // This tests that nodes are rendered in x-coordinate order (left-to-right)
6 let mut dag = DAG::new();
7
8 // Level 0: Root nodes
9 dag.add_node(1, "LeftRoot");
10 dag.add_node(2, "RightRoot");
11
12 // Level 1: Middle nodes - initially in median order
13 dag.add_node(3, "A");
14 dag.add_node(4, "B");
15 dag.add_node(5, "C");
16
17 // Level 2: Children clustered on opposite sides
18 // LeftRoot connects to far-right children
19 dag.add_edge(1, 6);
20 dag.add_edge(1, 7);
21
22 // RightRoot connects to far-left children
23 dag.add_edge(2, 8);
24 dag.add_edge(2, 9);
25
26 // Middle nodes connect normally
27 dag.add_edge(3, 6);
28 dag.add_edge(4, 8);
29 dag.add_edge(5, 9);
30
31 dag.add_node(6, "X");
32 dag.add_node(7, "Y");
33 dag.add_node(8, "P");
34 dag.add_node(9, "Q");
35
36 println!("{}", dag.render());
37
38 println!("\n--- Analysis ---");
39 println!("Level 0: LeftRoot should be on left, RightRoot on right");
40 println!("Level 2: Nodes should be ordered left-to-right by their x-coordinates");
41 println!("Spacing should always appear before the correct node, not after");
42}3fn main() {
4 let mut dag = DAG::new();
5
6 dag.add_node(1, "L");
7 dag.add_node(2, "R");
8 dag.add_node(3, "X");
9 dag.add_node(4, "Y");
10
11 // Create skewed children: L connects to rightmost, R connects to leftmost
12 dag.add_edge(1, 4); // L -> Y (right child)
13 dag.add_edge(2, 3); // R -> X (left child)
14
15 let output = dag.render();
16 println!("{}", output);
17
18 // Find line positions of each node
19 let lines: Vec<&str> = output.lines().collect();
20 for (idx, line) in lines.iter().enumerate() {
21 println!("Line {}: '{}'", idx, line);
22 }
23
24 let find_node = |name: &str| -> (usize, usize) {
25 for (line_idx, line) in lines.iter().enumerate() {
26 if let Some(col) = line.find(name) {
27 return (line_idx, col);
28 }
29 }
30 panic!("Node {} not found", name);
31 };
32
33 let (l_line, l_col) = find_node("[L]");
34 let (r_line, r_col) = find_node("[R]");
35 let (x_line, x_col) = find_node("[X]");
36 let (y_line, y_col) = find_node("[Y]");
37
38 println!("\nPositions:");
39 println!("L: line {}, col {}", l_line, l_col);
40 println!("R: line {}, col {}", r_line, r_col);
41 println!("X: line {}, col {}", x_line, x_col);
42 println!("Y: line {}, col {}", y_line, y_col);
43}3fn main() {
4 println!("=== Cycle Detection Examples ===\n");
5
6 // Example 1: Simple cycle A → B → C → A
7 println!("1. Simple Cycle (A → B → C → A):");
8 let mut dag = DAG::new();
9 dag.add_node(1, "A");
10 dag.add_node(2, "B");
11 dag.add_node(3, "C");
12 dag.add_edge(1, 2); // A → B
13 dag.add_edge(2, 3); // B → C
14 dag.add_edge(3, 1); // C → A (creates cycle!)
15
16 println!("{}\n", dag.render());
17
18 // Example 2: Self-referencing cycle
19 println!("2. Self-Reference (A → A):");
20 let mut dag = DAG::new();
21 dag.add_node(1, "SelfRef");
22 dag.add_edge(1, 1); // Points to itself
23
24 println!("{}\n", dag.render());
25
26 // Example 3: Longer cycle chain
27 println!("3. Longer Cycle (E1 → E2 → E3 → E4 → E2):");
28 let mut dag = DAG::new();
29 dag.add_node(1, "Error1");
30 dag.add_node(2, "Error2");
31 dag.add_node(3, "Error3");
32 dag.add_node(4, "Error4");
33 dag.add_edge(1, 2); // E1 → E2
34 dag.add_edge(2, 3); // E2 → E3
35 dag.add_edge(3, 4); // E3 → E4
36 dag.add_edge(4, 2); // E4 → E2 (cycle!)
37
38 println!("{}\n", dag.render());
39
40 // Example 4: Valid DAG (no cycle)
41 println!("4. Valid DAG - No Cycle:");
42 let dag = DAG::from_edges(
43 &[(1, "Valid1"), (2, "Valid2"), (3, "Valid3")],
44 &[(1, 2), (2, 3)],
45 );
46
47 println!("{}", dag.render());
48}7fn main() {
8 println!("=== Cross-Level Edge Examples ===\n");
9
10 // Example 1: Simple cross-level edge
11 println!("1. Simple cross-level (Root -> Middle -> End, plus Root -> End directly):");
12 let mut dag = DAG::new();
13 dag.add_node(1, "Root");
14 dag.add_node(2, "Middle");
15 dag.add_node(3, "End");
16
17 dag.add_edge(1, 2); // Root -> Middle
18 dag.add_edge(2, 3); // Middle -> End
19 dag.add_edge(1, 3); // Root -> End (cross-level!)
20
21 println!("{}\n", dag.render());
22
23 // Example 2: Multiple cross-level edges
24 println!("2. Multiple cross-level edges:");
25 let mut dag = DAG::new();
26 dag.add_node(1, "A");
27 dag.add_node(2, "B");
28 dag.add_node(3, "C");
29 dag.add_node(4, "D");
30
31 dag.add_edge(1, 2); // Level 0 -> 1
32 dag.add_edge(2, 3); // Level 1 -> 2
33 dag.add_edge(3, 4); // Level 2 -> 3
34 dag.add_edge(1, 4); // Level 0 -> 3 (cross-level!)
35
36 println!("{}\n", dag.render());
37
38 // Example 3: Complex with multiple paths
39 println!("3. Complex graph with shortcuts:");
40 let mut dag = DAG::new();
41 dag.add_node(1, "Start");
42 dag.add_node(2, "Parse");
43 dag.add_node(3, "Compile");
44 dag.add_node(4, "Link");
45 dag.add_node(5, "Done");
46
47 dag.add_edge(1, 2); // Normal flow
48 dag.add_edge(2, 3);
49 dag.add_edge(3, 4);
50 dag.add_edge(4, 5);
51 dag.add_edge(1, 5); // Direct shortcut from Start to Done!
52
53 println!("{}\n", dag.render());
54}Sourcepub fn from_edges(nodes: &[(usize, &'a str)], edges: &[(usize, usize)]) -> Self
pub fn from_edges(nodes: &[(usize, &'a str)], edges: &[(usize, usize)]) -> Self
Create a DAG from pre-defined nodes and edges (batch construction).
This is more efficient than using the builder API for static graphs.
§Examples
use ascii_dag::graph::DAG;
let dag = DAG::from_edges(
&[(1, "A"), (2, "B"), (3, "C")],
&[(1, 2), (2, 3)]
);Examples found in repository?
More examples
3fn main() {
4 println!("=== Basic Usage Examples ===\n");
5
6 // Example 1: Simple chain
7 println!("1. Simple Chain (A -> B -> C):");
8 let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
9 println!("{}\n", dag.render());
10
11 // Example 2: Diamond pattern
12 println!("2. Diamond Pattern:");
13 let dag = DAG::from_edges(
14 &[(1, "Root"), (2, "Left"), (3, "Right"), (4, "Merge")],
15 &[(1, 2), (1, 3), (2, 4), (3, 4)],
16 );
17 println!("{}\n", dag.render());
18
19 // Example 3: Builder API
20 println!("3. Builder API:");
21 let mut dag = DAG::new();
22 dag.add_node(1, "Parse");
23 dag.add_node(2, "Compile");
24 dag.add_node(3, "Link");
25 dag.add_edge(1, 2);
26 dag.add_edge(2, 3);
27 println!("{}\n", dag.render());
28
29 // Example 4: Multi-convergence
30 println!("4. Multi-Convergence:");
31 let dag = DAG::from_edges(
32 &[(1, "E1"), (2, "E2"), (3, "E3"), (4, "Final")],
33 &[(1, 4), (2, 4), (3, 4)],
34 );
35 println!("{}\n", dag.render());
36}3fn main() {
4 println!("=== Testing Parallel Chains ===\n");
5
6 // Two independent chains
7 let dag = DAG::from_edges(
8 &[
9 (1, "Chain1-A"),
10 (2, "Chain1-B"),
11 (3, "Chain1-C"),
12 (4, "Chain2-A"),
13 (5, "Chain2-B"),
14 (6, "Chain2-C"),
15 ],
16 &[
17 (1, 2),
18 (2, 3), // Chain 1
19 (4, 5),
20 (5, 6), // Chain 2 (no connection to chain 1)
21 ],
22 );
23
24 println!("Two parallel chains:");
25 println!("{}", dag.render());
26
27 // Three independent chains
28 let dag = DAG::from_edges(
29 &[
30 (1, "A1"),
31 (2, "A2"),
32 (3, "B1"),
33 (4, "B2"),
34 (5, "C1"),
35 (6, "C2"),
36 ],
37 &[(1, 2), (3, 4), (5, 6)],
38 );
39
40 println!("\nThree parallel chains:");
41 println!("{}", dag.render());
42
43 // Single chain (control)
44 let dag = DAG::from_edges(&[(1, "X"), (2, "Y"), (3, "Z")], &[(1, 2), (2, 3)]);
45
46 println!("\nSingle chain (control):");
47 println!("{}", dag.render());
48}3fn main() {
4 println!("=== Cycle Detection Examples ===\n");
5
6 // Example 1: Simple cycle A → B → C → A
7 println!("1. Simple Cycle (A → B → C → A):");
8 let mut dag = DAG::new();
9 dag.add_node(1, "A");
10 dag.add_node(2, "B");
11 dag.add_node(3, "C");
12 dag.add_edge(1, 2); // A → B
13 dag.add_edge(2, 3); // B → C
14 dag.add_edge(3, 1); // C → A (creates cycle!)
15
16 println!("{}\n", dag.render());
17
18 // Example 2: Self-referencing cycle
19 println!("2. Self-Reference (A → A):");
20 let mut dag = DAG::new();
21 dag.add_node(1, "SelfRef");
22 dag.add_edge(1, 1); // Points to itself
23
24 println!("{}\n", dag.render());
25
26 // Example 3: Longer cycle chain
27 println!("3. Longer Cycle (E1 → E2 → E3 → E4 → E2):");
28 let mut dag = DAG::new();
29 dag.add_node(1, "Error1");
30 dag.add_node(2, "Error2");
31 dag.add_node(3, "Error3");
32 dag.add_node(4, "Error4");
33 dag.add_edge(1, 2); // E1 → E2
34 dag.add_edge(2, 3); // E2 → E3
35 dag.add_edge(3, 4); // E3 → E4
36 dag.add_edge(4, 2); // E4 → E2 (cycle!)
37
38 println!("{}\n", dag.render());
39
40 // Example 4: Valid DAG (no cycle)
41 println!("4. Valid DAG - No Cycle:");
42 let dag = DAG::from_edges(
43 &[(1, "Valid1"), (2, "Valid2"), (3, "Valid3")],
44 &[(1, 2), (2, 3)],
45 );
46
47 println!("{}", dag.render());
48}3fn main() {
4 println!("=== Error Chain Visualization ===\n");
5
6 // Simulate an error dependency chain
7 // FileNotFound -> ParseError -> CompilationFailed -> BuildFailed
8
9 let mut dag = DAG::new();
10
11 dag.add_node(1, "FileNotFound");
12 dag.add_node(2, "ParseError");
13 dag.add_node(3, "CompilationFailed");
14 dag.add_node(4, "BuildFailed");
15
16 dag.add_edge(1, 2); // FileNotFound caused ParseError
17 dag.add_edge(2, 3); // ParseError caused CompilationFailed
18 dag.add_edge(3, 4); // CompilationFailed caused BuildFailed
19
20 println!("Error Dependency Chain:");
21 println!("{}", dag.render());
22
23 // More complex error scenario
24 println!("\n=== Complex Error Scenario ===\n");
25
26 let dag = DAG::from_edges(
27 &[
28 (1, "ConfigMissing"),
29 (2, "DBConnFail"),
30 (3, "AuthFail"),
31 (4, "InitError"),
32 (5, "StartupFail"),
33 ],
34 &[
35 (1, 2), // ConfigMissing -> DBConnFail
36 (1, 3), // ConfigMissing -> AuthFail
37 (2, 4), // DBConnFail -> InitError
38 (3, 4), // AuthFail -> InitError
39 (4, 5), // InitError -> StartupFail
40 ],
41 );
42
43 println!("Multiple Error Paths:");
44 println!("{}", dag.render());
45
46 // Check for cycles (should not have any)
47 if dag.has_cycle() {
48 println!("\n⚠️ WARNING: Circular error dependency detected!");
49 } else {
50 println!("\n✓ No circular dependencies");
51 }
52}3fn main() {
4 println!("=== Stress Test: Breaking the Renderer ===\n");
5
6 // Test 1: Very long vs very short labels
7 println!("1. Mixed Label Lengths:");
8 let dag = DAG::from_edges(
9 &[
10 (1, "A"),
11 (2, "VeryVeryVeryLongErrorNameThatShouldBreakAlignment"),
12 (3, "B"),
13 (4, "AnotherExtremelyLongDiagnosticMessageHere"),
14 (5, "C"),
15 ],
16 &[
17 (1, 2), // Short -> Long
18 (2, 3), // Long -> Short
19 (3, 4), // Short -> Long
20 (4, 5), // Long -> Short
21 ],
22 );
23 println!("{}\n", dag.render());
24
25 // Test 2: Extreme convergence (many -> one)
26 println!("2. Extreme Convergence (10 errors -> 1):");
27 let mut dag = DAG::new();
28 dag.add_node(1, "E1");
29 dag.add_node(2, "E2");
30 dag.add_node(3, "E3");
31 dag.add_node(4, "E4");
32 dag.add_node(5, "E5");
33 dag.add_node(6, "E6");
34 dag.add_node(7, "E7");
35 dag.add_node(8, "E8");
36 dag.add_node(9, "E9");
37 dag.add_node(10, "E10");
38 dag.add_node(11, "Final");
39
40 for i in 1..=10 {
41 dag.add_edge(i, 11);
42 }
43 println!("{}\n", dag.render());
44
45 // Test 3: Extreme divergence (one -> many)
46 println!("3. Extreme Divergence (1 -> 8):");
47 let dag = DAG::from_edges(
48 &[
49 (1, "Root"),
50 (2, "Child1"),
51 (3, "Child2"),
52 (4, "Child3"),
53 (5, "Child4"),
54 (6, "Child5"),
55 (7, "Child6"),
56 (8, "Child7"),
57 (9, "Child8"),
58 ],
59 &[
60 (1, 2),
61 (1, 3),
62 (1, 4),
63 (1, 5),
64 (1, 6),
65 (1, 7),
66 (1, 8),
67 (1, 9),
68 ],
69 );
70 println!("{}\n", dag.render());
71
72 // Test 4: Complex multi-layer DAG
73 println!("4. Complex Multi-Layer DAG:");
74 let dag = DAG::from_edges(
75 &[
76 (1, "L1A"),
77 (2, "L1B"),
78 (3, "L1C"), // Layer 1: 3 nodes
79 (4, "L2A"),
80 (5, "L2B"), // Layer 2: 2 nodes
81 (6, "L3A"),
82 (7, "L3B"),
83 (8, "L3C"), // Layer 3: 3 nodes
84 (9, "Final"), // Layer 4: 1 node
85 ],
86 &[
87 // Layer 1 -> Layer 2
88 (1, 4),
89 (2, 4),
90 (3, 5),
91 // Layer 2 -> Layer 3
92 (4, 6),
93 (4, 7),
94 (5, 7),
95 (5, 8),
96 // Layer 3 -> Layer 4
97 (6, 9),
98 (7, 9),
99 (8, 9),
100 ],
101 );
102 println!("{}\n", dag.render());
103
104 // Test 5: Single character labels
105 println!("5. Single Character Labels:");
106 let dag = DAG::from_edges(
107 &[(1, "A"), (2, "B"), (3, "C"), (4, "D"), (5, "E")],
108 &[(1, 3), (2, 3), (3, 4), (3, 5)],
109 );
110 println!("{}\n", dag.render());
111
112 // Test 6: Empty label (edge case)
113 println!("6. Empty/Minimal Labels:");
114 let dag = DAG::from_edges(&[(1, ""), (2, "X"), (3, "")], &[(1, 2), (2, 3)]);
115 println!("{}\n", dag.render());
116
117 // Test 7: Unicode in labels
118 println!("7. Unicode Characters in Labels:");
119 let dag = DAG::from_edges(
120 &[(1, "🔴 Error"), (2, "⚠️ Warning"), (3, "✓ Fixed")],
121 &[(1, 2), (2, 3)],
122 );
123 println!("{}\n", dag.render());
124
125 // Test 8: Mixed long and short with convergence
126 println!("8. Mixed Lengths + Convergence:");
127 let dag = DAG::from_edges(
128 &[
129 (1, "ShortError1"),
130 (2, "ThisIsAnExtremelyLongErrorMessageThatGoesOnAndOn"),
131 (3, "Err3"),
132 (4, "AnotherVeryLongDiagnosticNameHereForTesting"),
133 (5, "Result"),
134 ],
135 &[(1, 5), (2, 5), (3, 5), (4, 5)],
136 );
137 println!("{}\n", dag.render());
138
139 // Test 9: Deep nesting
140 println!("9. Deep Nesting (10 levels):");
141 let dag = DAG::from_edges(
142 &[
143 (1, "Level1"),
144 (2, "Level2"),
145 (3, "Level3"),
146 (4, "Level4"),
147 (5, "Level5"),
148 (6, "Level6"),
149 (7, "Level7"),
150 (8, "Level8"),
151 (9, "Level9"),
152 (10, "Level10"),
153 ],
154 &[
155 (1, 2),
156 (2, 3),
157 (3, 4),
158 (4, 5),
159 (5, 6),
160 (6, 7),
161 (7, 8),
162 (8, 9),
163 (9, 10),
164 ],
165 );
166 println!("{}\n", dag.render());
167
168 // Test 10: Wide graph (5 parallel chains)
169 println!("10. Wide Graph (5 parallel chains):");
170 let dag = DAG::from_edges(
171 &[
172 (1, "C0L0"),
173 (2, "C0L1"),
174 (3, "C0L2"),
175 (4, "C1L0"),
176 (5, "C1L1"),
177 (6, "C1L2"),
178 (7, "C2L0"),
179 (8, "C2L1"),
180 (9, "C2L2"),
181 (10, "C3L0"),
182 (11, "C3L1"),
183 (12, "C3L2"),
184 (13, "C4L0"),
185 (14, "C4L1"),
186 (15, "C4L2"),
187 ],
188 &[
189 (1, 2),
190 (2, 3),
191 (4, 5),
192 (5, 6),
193 (7, 8),
194 (8, 9),
195 (10, 11),
196 (11, 12),
197 (13, 14),
198 (14, 15),
199 ],
200 );
201 println!("{}\n", dag.render());
202
203 // Test 11: Diamond within diamond
204 println!("11. Diamond within Diamond:");
205 let dag = DAG::from_edges(
206 &[
207 (1, "Root"),
208 (2, "L1"),
209 (3, "R1"),
210 (4, "L2"),
211 (5, "R2"),
212 (6, "L3"),
213 (7, "R3"),
214 (8, "Merge1"),
215 (9, "Merge2"),
216 ],
217 &[
218 (1, 2),
219 (1, 3), // Root splits
220 (2, 4),
221 (2, 5), // Left splits
222 (3, 6),
223 (3, 7), // Right splits
224 (4, 8),
225 (5, 8), // Left converges
226 (6, 9),
227 (7, 9), // Right converges
228 ],
229 );
230 println!("{}\n", dag.render());
231
232 println!("=== Stress Test Complete ===");
233}Sourcepub fn set_render_mode(&mut self, mode: RenderMode)
pub fn set_render_mode(&mut self, mode: RenderMode)
Set the rendering mode.
§Examples
use ascii_dag::graph::{DAG, RenderMode};
let mut dag = DAG::new();
dag.set_render_mode(RenderMode::Horizontal);Sourcepub fn with_mode(mode: RenderMode) -> Self
pub fn with_mode(mode: RenderMode) -> Self
Create a DAG with a specific render mode.
§Examples
use ascii_dag::graph::{DAG, RenderMode};
let dag = DAG::with_mode(RenderMode::Horizontal);Sourcepub fn add_node(&mut self, id: usize, label: &'a str)
pub fn add_node(&mut self, id: usize, label: &'a str)
Add a node to the DAG.
If the node was previously auto-created by add_edge, this will promote it
by setting its label and removing the auto-created flag.
§Examples
use ascii_dag::graph::DAG;
let mut dag = DAG::new();
dag.add_node(1, "MyNode");Examples found in repository?
3fn main() {
4 let mut dag = DAG::new();
5
6 println!("=== Test: Auto-created Node Promotion ===\n");
7
8 dag.add_node(1, "Start");
9 dag.add_edge(1, 2); // Auto-creates node 2
10
11 println!("Before promotion (node 2 is auto-created):");
12 println!("{}\n", dag.render());
13
14 // Now promote node 2
15 dag.add_node(2, "Promoted");
16
17 println!("After promotion (node 2 now has label 'Promoted'):");
18 println!("{}\n", dag.render());
19
20 println!("✓ Node 2 successfully promoted from ⟨2⟩ to [Promoted]");
21}More examples
3fn main() {
4 println!("=== Basic Usage Examples ===\n");
5
6 // Example 1: Simple chain
7 println!("1. Simple Chain (A -> B -> C):");
8 let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
9 println!("{}\n", dag.render());
10
11 // Example 2: Diamond pattern
12 println!("2. Diamond Pattern:");
13 let dag = DAG::from_edges(
14 &[(1, "Root"), (2, "Left"), (3, "Right"), (4, "Merge")],
15 &[(1, 2), (1, 3), (2, 4), (3, 4)],
16 );
17 println!("{}\n", dag.render());
18
19 // Example 3: Builder API
20 println!("3. Builder API:");
21 let mut dag = DAG::new();
22 dag.add_node(1, "Parse");
23 dag.add_node(2, "Compile");
24 dag.add_node(3, "Link");
25 dag.add_edge(1, 2);
26 dag.add_edge(2, 3);
27 println!("{}\n", dag.render());
28
29 // Example 4: Multi-convergence
30 println!("4. Multi-Convergence:");
31 let dag = DAG::from_edges(
32 &[(1, "E1"), (2, "E2"), (3, "E3"), (4, "Final")],
33 &[(1, 4), (2, 4), (3, 4)],
34 );
35 println!("{}\n", dag.render());
36}3fn main() {
4 // Create a graph where a level's children are clustered on opposite sides
5 // This tests that nodes are rendered in x-coordinate order (left-to-right)
6 let mut dag = DAG::new();
7
8 // Level 0: Root nodes
9 dag.add_node(1, "LeftRoot");
10 dag.add_node(2, "RightRoot");
11
12 // Level 1: Middle nodes - initially in median order
13 dag.add_node(3, "A");
14 dag.add_node(4, "B");
15 dag.add_node(5, "C");
16
17 // Level 2: Children clustered on opposite sides
18 // LeftRoot connects to far-right children
19 dag.add_edge(1, 6);
20 dag.add_edge(1, 7);
21
22 // RightRoot connects to far-left children
23 dag.add_edge(2, 8);
24 dag.add_edge(2, 9);
25
26 // Middle nodes connect normally
27 dag.add_edge(3, 6);
28 dag.add_edge(4, 8);
29 dag.add_edge(5, 9);
30
31 dag.add_node(6, "X");
32 dag.add_node(7, "Y");
33 dag.add_node(8, "P");
34 dag.add_node(9, "Q");
35
36 println!("{}", dag.render());
37
38 println!("\n--- Analysis ---");
39 println!("Level 0: LeftRoot should be on left, RightRoot on right");
40 println!("Level 2: Nodes should be ordered left-to-right by their x-coordinates");
41 println!("Spacing should always appear before the correct node, not after");
42}3fn main() {
4 let mut dag = DAG::new();
5
6 dag.add_node(1, "L");
7 dag.add_node(2, "R");
8 dag.add_node(3, "X");
9 dag.add_node(4, "Y");
10
11 // Create skewed children: L connects to rightmost, R connects to leftmost
12 dag.add_edge(1, 4); // L -> Y (right child)
13 dag.add_edge(2, 3); // R -> X (left child)
14
15 let output = dag.render();
16 println!("{}", output);
17
18 // Find line positions of each node
19 let lines: Vec<&str> = output.lines().collect();
20 for (idx, line) in lines.iter().enumerate() {
21 println!("Line {}: '{}'", idx, line);
22 }
23
24 let find_node = |name: &str| -> (usize, usize) {
25 for (line_idx, line) in lines.iter().enumerate() {
26 if let Some(col) = line.find(name) {
27 return (line_idx, col);
28 }
29 }
30 panic!("Node {} not found", name);
31 };
32
33 let (l_line, l_col) = find_node("[L]");
34 let (r_line, r_col) = find_node("[R]");
35 let (x_line, x_col) = find_node("[X]");
36 let (y_line, y_col) = find_node("[Y]");
37
38 println!("\nPositions:");
39 println!("L: line {}, col {}", l_line, l_col);
40 println!("R: line {}, col {}", r_line, r_col);
41 println!("X: line {}, col {}", x_line, x_col);
42 println!("Y: line {}, col {}", y_line, y_col);
43}3fn main() {
4 println!("=== Cycle Detection Examples ===\n");
5
6 // Example 1: Simple cycle A → B → C → A
7 println!("1. Simple Cycle (A → B → C → A):");
8 let mut dag = DAG::new();
9 dag.add_node(1, "A");
10 dag.add_node(2, "B");
11 dag.add_node(3, "C");
12 dag.add_edge(1, 2); // A → B
13 dag.add_edge(2, 3); // B → C
14 dag.add_edge(3, 1); // C → A (creates cycle!)
15
16 println!("{}\n", dag.render());
17
18 // Example 2: Self-referencing cycle
19 println!("2. Self-Reference (A → A):");
20 let mut dag = DAG::new();
21 dag.add_node(1, "SelfRef");
22 dag.add_edge(1, 1); // Points to itself
23
24 println!("{}\n", dag.render());
25
26 // Example 3: Longer cycle chain
27 println!("3. Longer Cycle (E1 → E2 → E3 → E4 → E2):");
28 let mut dag = DAG::new();
29 dag.add_node(1, "Error1");
30 dag.add_node(2, "Error2");
31 dag.add_node(3, "Error3");
32 dag.add_node(4, "Error4");
33 dag.add_edge(1, 2); // E1 → E2
34 dag.add_edge(2, 3); // E2 → E3
35 dag.add_edge(3, 4); // E3 → E4
36 dag.add_edge(4, 2); // E4 → E2 (cycle!)
37
38 println!("{}\n", dag.render());
39
40 // Example 4: Valid DAG (no cycle)
41 println!("4. Valid DAG - No Cycle:");
42 let dag = DAG::from_edges(
43 &[(1, "Valid1"), (2, "Valid2"), (3, "Valid3")],
44 &[(1, 2), (2, 3)],
45 );
46
47 println!("{}", dag.render());
48}7fn main() {
8 println!("=== Cross-Level Edge Examples ===\n");
9
10 // Example 1: Simple cross-level edge
11 println!("1. Simple cross-level (Root -> Middle -> End, plus Root -> End directly):");
12 let mut dag = DAG::new();
13 dag.add_node(1, "Root");
14 dag.add_node(2, "Middle");
15 dag.add_node(3, "End");
16
17 dag.add_edge(1, 2); // Root -> Middle
18 dag.add_edge(2, 3); // Middle -> End
19 dag.add_edge(1, 3); // Root -> End (cross-level!)
20
21 println!("{}\n", dag.render());
22
23 // Example 2: Multiple cross-level edges
24 println!("2. Multiple cross-level edges:");
25 let mut dag = DAG::new();
26 dag.add_node(1, "A");
27 dag.add_node(2, "B");
28 dag.add_node(3, "C");
29 dag.add_node(4, "D");
30
31 dag.add_edge(1, 2); // Level 0 -> 1
32 dag.add_edge(2, 3); // Level 1 -> 2
33 dag.add_edge(3, 4); // Level 2 -> 3
34 dag.add_edge(1, 4); // Level 0 -> 3 (cross-level!)
35
36 println!("{}\n", dag.render());
37
38 // Example 3: Complex with multiple paths
39 println!("3. Complex graph with shortcuts:");
40 let mut dag = DAG::new();
41 dag.add_node(1, "Start");
42 dag.add_node(2, "Parse");
43 dag.add_node(3, "Compile");
44 dag.add_node(4, "Link");
45 dag.add_node(5, "Done");
46
47 dag.add_edge(1, 2); // Normal flow
48 dag.add_edge(2, 3);
49 dag.add_edge(3, 4);
50 dag.add_edge(4, 5);
51 dag.add_edge(1, 5); // Direct shortcut from Start to Done!
52
53 println!("{}\n", dag.render());
54}Sourcepub fn add_edge(&mut self, from: usize, to: usize)
pub fn add_edge(&mut self, from: usize, to: usize)
Add an edge from one node to another.
If either node doesn’t exist, it will be auto-created as a placeholder.
You can later call add_node to provide a label for auto-created nodes.
§Examples
use ascii_dag::graph::DAG;
let mut dag = DAG::new();
dag.add_node(1, "A");
dag.add_node(2, "B");
dag.add_edge(1, 2); // A -> BExamples found in repository?
3fn main() {
4 let mut dag = DAG::new();
5
6 println!("=== Test: Auto-created Node Promotion ===\n");
7
8 dag.add_node(1, "Start");
9 dag.add_edge(1, 2); // Auto-creates node 2
10
11 println!("Before promotion (node 2 is auto-created):");
12 println!("{}\n", dag.render());
13
14 // Now promote node 2
15 dag.add_node(2, "Promoted");
16
17 println!("After promotion (node 2 now has label 'Promoted'):");
18 println!("{}\n", dag.render());
19
20 println!("✓ Node 2 successfully promoted from ⟨2⟩ to [Promoted]");
21}More examples
3fn main() {
4 println!("=== Basic Usage Examples ===\n");
5
6 // Example 1: Simple chain
7 println!("1. Simple Chain (A -> B -> C):");
8 let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
9 println!("{}\n", dag.render());
10
11 // Example 2: Diamond pattern
12 println!("2. Diamond Pattern:");
13 let dag = DAG::from_edges(
14 &[(1, "Root"), (2, "Left"), (3, "Right"), (4, "Merge")],
15 &[(1, 2), (1, 3), (2, 4), (3, 4)],
16 );
17 println!("{}\n", dag.render());
18
19 // Example 3: Builder API
20 println!("3. Builder API:");
21 let mut dag = DAG::new();
22 dag.add_node(1, "Parse");
23 dag.add_node(2, "Compile");
24 dag.add_node(3, "Link");
25 dag.add_edge(1, 2);
26 dag.add_edge(2, 3);
27 println!("{}\n", dag.render());
28
29 // Example 4: Multi-convergence
30 println!("4. Multi-Convergence:");
31 let dag = DAG::from_edges(
32 &[(1, "E1"), (2, "E2"), (3, "E3"), (4, "Final")],
33 &[(1, 4), (2, 4), (3, 4)],
34 );
35 println!("{}\n", dag.render());
36}3fn main() {
4 // Create a graph where a level's children are clustered on opposite sides
5 // This tests that nodes are rendered in x-coordinate order (left-to-right)
6 let mut dag = DAG::new();
7
8 // Level 0: Root nodes
9 dag.add_node(1, "LeftRoot");
10 dag.add_node(2, "RightRoot");
11
12 // Level 1: Middle nodes - initially in median order
13 dag.add_node(3, "A");
14 dag.add_node(4, "B");
15 dag.add_node(5, "C");
16
17 // Level 2: Children clustered on opposite sides
18 // LeftRoot connects to far-right children
19 dag.add_edge(1, 6);
20 dag.add_edge(1, 7);
21
22 // RightRoot connects to far-left children
23 dag.add_edge(2, 8);
24 dag.add_edge(2, 9);
25
26 // Middle nodes connect normally
27 dag.add_edge(3, 6);
28 dag.add_edge(4, 8);
29 dag.add_edge(5, 9);
30
31 dag.add_node(6, "X");
32 dag.add_node(7, "Y");
33 dag.add_node(8, "P");
34 dag.add_node(9, "Q");
35
36 println!("{}", dag.render());
37
38 println!("\n--- Analysis ---");
39 println!("Level 0: LeftRoot should be on left, RightRoot on right");
40 println!("Level 2: Nodes should be ordered left-to-right by their x-coordinates");
41 println!("Spacing should always appear before the correct node, not after");
42}3fn main() {
4 let mut dag = DAG::new();
5
6 dag.add_node(1, "L");
7 dag.add_node(2, "R");
8 dag.add_node(3, "X");
9 dag.add_node(4, "Y");
10
11 // Create skewed children: L connects to rightmost, R connects to leftmost
12 dag.add_edge(1, 4); // L -> Y (right child)
13 dag.add_edge(2, 3); // R -> X (left child)
14
15 let output = dag.render();
16 println!("{}", output);
17
18 // Find line positions of each node
19 let lines: Vec<&str> = output.lines().collect();
20 for (idx, line) in lines.iter().enumerate() {
21 println!("Line {}: '{}'", idx, line);
22 }
23
24 let find_node = |name: &str| -> (usize, usize) {
25 for (line_idx, line) in lines.iter().enumerate() {
26 if let Some(col) = line.find(name) {
27 return (line_idx, col);
28 }
29 }
30 panic!("Node {} not found", name);
31 };
32
33 let (l_line, l_col) = find_node("[L]");
34 let (r_line, r_col) = find_node("[R]");
35 let (x_line, x_col) = find_node("[X]");
36 let (y_line, y_col) = find_node("[Y]");
37
38 println!("\nPositions:");
39 println!("L: line {}, col {}", l_line, l_col);
40 println!("R: line {}, col {}", r_line, r_col);
41 println!("X: line {}, col {}", x_line, x_col);
42 println!("Y: line {}, col {}", y_line, y_col);
43}3fn main() {
4 println!("=== Cycle Detection Examples ===\n");
5
6 // Example 1: Simple cycle A → B → C → A
7 println!("1. Simple Cycle (A → B → C → A):");
8 let mut dag = DAG::new();
9 dag.add_node(1, "A");
10 dag.add_node(2, "B");
11 dag.add_node(3, "C");
12 dag.add_edge(1, 2); // A → B
13 dag.add_edge(2, 3); // B → C
14 dag.add_edge(3, 1); // C → A (creates cycle!)
15
16 println!("{}\n", dag.render());
17
18 // Example 2: Self-referencing cycle
19 println!("2. Self-Reference (A → A):");
20 let mut dag = DAG::new();
21 dag.add_node(1, "SelfRef");
22 dag.add_edge(1, 1); // Points to itself
23
24 println!("{}\n", dag.render());
25
26 // Example 3: Longer cycle chain
27 println!("3. Longer Cycle (E1 → E2 → E3 → E4 → E2):");
28 let mut dag = DAG::new();
29 dag.add_node(1, "Error1");
30 dag.add_node(2, "Error2");
31 dag.add_node(3, "Error3");
32 dag.add_node(4, "Error4");
33 dag.add_edge(1, 2); // E1 → E2
34 dag.add_edge(2, 3); // E2 → E3
35 dag.add_edge(3, 4); // E3 → E4
36 dag.add_edge(4, 2); // E4 → E2 (cycle!)
37
38 println!("{}\n", dag.render());
39
40 // Example 4: Valid DAG (no cycle)
41 println!("4. Valid DAG - No Cycle:");
42 let dag = DAG::from_edges(
43 &[(1, "Valid1"), (2, "Valid2"), (3, "Valid3")],
44 &[(1, 2), (2, 3)],
45 );
46
47 println!("{}", dag.render());
48}7fn main() {
8 println!("=== Cross-Level Edge Examples ===\n");
9
10 // Example 1: Simple cross-level edge
11 println!("1. Simple cross-level (Root -> Middle -> End, plus Root -> End directly):");
12 let mut dag = DAG::new();
13 dag.add_node(1, "Root");
14 dag.add_node(2, "Middle");
15 dag.add_node(3, "End");
16
17 dag.add_edge(1, 2); // Root -> Middle
18 dag.add_edge(2, 3); // Middle -> End
19 dag.add_edge(1, 3); // Root -> End (cross-level!)
20
21 println!("{}\n", dag.render());
22
23 // Example 2: Multiple cross-level edges
24 println!("2. Multiple cross-level edges:");
25 let mut dag = DAG::new();
26 dag.add_node(1, "A");
27 dag.add_node(2, "B");
28 dag.add_node(3, "C");
29 dag.add_node(4, "D");
30
31 dag.add_edge(1, 2); // Level 0 -> 1
32 dag.add_edge(2, 3); // Level 1 -> 2
33 dag.add_edge(3, 4); // Level 2 -> 3
34 dag.add_edge(1, 4); // Level 0 -> 3 (cross-level!)
35
36 println!("{}\n", dag.render());
37
38 // Example 3: Complex with multiple paths
39 println!("3. Complex graph with shortcuts:");
40 let mut dag = DAG::new();
41 dag.add_node(1, "Start");
42 dag.add_node(2, "Parse");
43 dag.add_node(3, "Compile");
44 dag.add_node(4, "Link");
45 dag.add_node(5, "Done");
46
47 dag.add_edge(1, 2); // Normal flow
48 dag.add_edge(2, 3);
49 dag.add_edge(3, 4);
50 dag.add_edge(4, 5);
51 dag.add_edge(1, 5); // Direct shortcut from Start to Done!
52
53 println!("{}\n", dag.render());
54}Sourcepub fn estimate_size(&self) -> usize
pub fn estimate_size(&self) -> usize
Estimate the buffer size needed for rendering.
Use this to pre-allocate a buffer for render_to.
§Examples
use ascii_dag::graph::DAG;
let dag = DAG::from_edges(
&[(1, "A"), (2, "B")],
&[(1, 2)]
);
let size = dag.estimate_size();
let mut buffer = String::with_capacity(size);
dag.render_to(&mut buffer);Source§impl<'a> DAG<'a>
impl<'a> DAG<'a>
Sourcepub fn render(&self) -> String
pub fn render(&self) -> String
Render the DAG to an ASCII string.
§Examples
use ascii_dag::graph::DAG;
let dag = DAG::from_edges(
&[(1, "Start"), (2, "End")],
&[(1, 2)]
);
let output = dag.render();
println!("{}", output);Examples found in repository?
More examples
3fn main() {
4 let mut dag = DAG::new();
5
6 println!("=== Test: Auto-created Node Promotion ===\n");
7
8 dag.add_node(1, "Start");
9 dag.add_edge(1, 2); // Auto-creates node 2
10
11 println!("Before promotion (node 2 is auto-created):");
12 println!("{}\n", dag.render());
13
14 // Now promote node 2
15 dag.add_node(2, "Promoted");
16
17 println!("After promotion (node 2 now has label 'Promoted'):");
18 println!("{}\n", dag.render());
19
20 println!("✓ Node 2 successfully promoted from ⟨2⟩ to [Promoted]");
21}3fn main() {
4 println!("=== Basic Usage Examples ===\n");
5
6 // Example 1: Simple chain
7 println!("1. Simple Chain (A -> B -> C):");
8 let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
9 println!("{}\n", dag.render());
10
11 // Example 2: Diamond pattern
12 println!("2. Diamond Pattern:");
13 let dag = DAG::from_edges(
14 &[(1, "Root"), (2, "Left"), (3, "Right"), (4, "Merge")],
15 &[(1, 2), (1, 3), (2, 4), (3, 4)],
16 );
17 println!("{}\n", dag.render());
18
19 // Example 3: Builder API
20 println!("3. Builder API:");
21 let mut dag = DAG::new();
22 dag.add_node(1, "Parse");
23 dag.add_node(2, "Compile");
24 dag.add_node(3, "Link");
25 dag.add_edge(1, 2);
26 dag.add_edge(2, 3);
27 println!("{}\n", dag.render());
28
29 // Example 4: Multi-convergence
30 println!("4. Multi-Convergence:");
31 let dag = DAG::from_edges(
32 &[(1, "E1"), (2, "E2"), (3, "E3"), (4, "Final")],
33 &[(1, 4), (2, 4), (3, 4)],
34 );
35 println!("{}\n", dag.render());
36}3fn main() {
4 println!("=== Testing Parallel Chains ===\n");
5
6 // Two independent chains
7 let dag = DAG::from_edges(
8 &[
9 (1, "Chain1-A"),
10 (2, "Chain1-B"),
11 (3, "Chain1-C"),
12 (4, "Chain2-A"),
13 (5, "Chain2-B"),
14 (6, "Chain2-C"),
15 ],
16 &[
17 (1, 2),
18 (2, 3), // Chain 1
19 (4, 5),
20 (5, 6), // Chain 2 (no connection to chain 1)
21 ],
22 );
23
24 println!("Two parallel chains:");
25 println!("{}", dag.render());
26
27 // Three independent chains
28 let dag = DAG::from_edges(
29 &[
30 (1, "A1"),
31 (2, "A2"),
32 (3, "B1"),
33 (4, "B2"),
34 (5, "C1"),
35 (6, "C2"),
36 ],
37 &[(1, 2), (3, 4), (5, 6)],
38 );
39
40 println!("\nThree parallel chains:");
41 println!("{}", dag.render());
42
43 // Single chain (control)
44 let dag = DAG::from_edges(&[(1, "X"), (2, "Y"), (3, "Z")], &[(1, 2), (2, 3)]);
45
46 println!("\nSingle chain (control):");
47 println!("{}", dag.render());
48}3fn main() {
4 // Create a graph where a level's children are clustered on opposite sides
5 // This tests that nodes are rendered in x-coordinate order (left-to-right)
6 let mut dag = DAG::new();
7
8 // Level 0: Root nodes
9 dag.add_node(1, "LeftRoot");
10 dag.add_node(2, "RightRoot");
11
12 // Level 1: Middle nodes - initially in median order
13 dag.add_node(3, "A");
14 dag.add_node(4, "B");
15 dag.add_node(5, "C");
16
17 // Level 2: Children clustered on opposite sides
18 // LeftRoot connects to far-right children
19 dag.add_edge(1, 6);
20 dag.add_edge(1, 7);
21
22 // RightRoot connects to far-left children
23 dag.add_edge(2, 8);
24 dag.add_edge(2, 9);
25
26 // Middle nodes connect normally
27 dag.add_edge(3, 6);
28 dag.add_edge(4, 8);
29 dag.add_edge(5, 9);
30
31 dag.add_node(6, "X");
32 dag.add_node(7, "Y");
33 dag.add_node(8, "P");
34 dag.add_node(9, "Q");
35
36 println!("{}", dag.render());
37
38 println!("\n--- Analysis ---");
39 println!("Level 0: LeftRoot should be on left, RightRoot on right");
40 println!("Level 2: Nodes should be ordered left-to-right by their x-coordinates");
41 println!("Spacing should always appear before the correct node, not after");
42}3fn main() {
4 let mut dag = DAG::new();
5
6 dag.add_node(1, "L");
7 dag.add_node(2, "R");
8 dag.add_node(3, "X");
9 dag.add_node(4, "Y");
10
11 // Create skewed children: L connects to rightmost, R connects to leftmost
12 dag.add_edge(1, 4); // L -> Y (right child)
13 dag.add_edge(2, 3); // R -> X (left child)
14
15 let output = dag.render();
16 println!("{}", output);
17
18 // Find line positions of each node
19 let lines: Vec<&str> = output.lines().collect();
20 for (idx, line) in lines.iter().enumerate() {
21 println!("Line {}: '{}'", idx, line);
22 }
23
24 let find_node = |name: &str| -> (usize, usize) {
25 for (line_idx, line) in lines.iter().enumerate() {
26 if let Some(col) = line.find(name) {
27 return (line_idx, col);
28 }
29 }
30 panic!("Node {} not found", name);
31 };
32
33 let (l_line, l_col) = find_node("[L]");
34 let (r_line, r_col) = find_node("[R]");
35 let (x_line, x_col) = find_node("[X]");
36 let (y_line, y_col) = find_node("[Y]");
37
38 println!("\nPositions:");
39 println!("L: line {}, col {}", l_line, l_col);
40 println!("R: line {}, col {}", r_line, r_col);
41 println!("X: line {}, col {}", x_line, x_col);
42 println!("Y: line {}, col {}", y_line, y_col);
43}