DAG

Struct DAG 

Source
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>

Source

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?
examples/error_chain.rs (line 47)
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>

Source

pub fn new() -> Self

Create a new empty DAG.

§Examples
use ascii_dag::graph::DAG;
let dag = DAG::new();
Examples found in repository?
examples/test_promotion.rs (line 4)
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
Hide additional examples
examples/basic.rs (line 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}
examples/skewed_children.rs (line 6)
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}
examples/test_skewed.rs (line 4)
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}
examples/cycles.rs (line 8)
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}
examples/cross_level.rs (line 12)
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}
Source

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?
examples/minimal.rs (line 4)
3fn main() {
4    let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
5
6    println!("{}", dag.render());
7}
More examples
Hide additional examples
examples/basic.rs (line 8)
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}
examples/parallel_test.rs (lines 7-22)
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}
examples/cycles.rs (lines 42-45)
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}
examples/error_chain.rs (lines 26-41)
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}
examples/stress_test.rs (lines 8-22)
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}
Source

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);
Source

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);
Source

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?
examples/test_promotion.rs (line 8)
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
Hide additional examples
examples/basic.rs (line 22)
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}
examples/skewed_children.rs (line 9)
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}
examples/test_skewed.rs (line 6)
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}
examples/cycles.rs (line 9)
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}
examples/cross_level.rs (line 13)
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}
Source

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 -> B
Examples found in repository?
examples/test_promotion.rs (line 9)
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
Hide additional examples
examples/basic.rs (line 25)
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}
examples/skewed_children.rs (line 19)
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}
examples/test_skewed.rs (line 12)
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}
examples/cycles.rs (line 12)
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}
examples/cross_level.rs (line 17)
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}
Source

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>

Source

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?
examples/minimal.rs (line 6)
3fn main() {
4    let dag = DAG::from_edges(&[(1, "A"), (2, "B"), (3, "C")], &[(1, 2), (2, 3)]);
5
6    println!("{}", dag.render());
7}
More examples
Hide additional examples
examples/test_promotion.rs (line 12)
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}
examples/basic.rs (line 9)
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}
examples/parallel_test.rs (line 25)
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}
examples/skewed_children.rs (line 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}
examples/test_skewed.rs (line 15)
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}
Source

pub fn render_to(&self, output: &mut String)

Render the DAG into a provided buffer (zero-allocation).

§Examples
use ascii_dag::graph::DAG;

let dag = DAG::from_edges(
    &[(1, "A")],
    &[]
);

let mut buffer = String::new();
dag.render_to(&mut buffer);
assert!(!buffer.is_empty());

Trait Implementations§

Source§

impl<'a> Clone for DAG<'a>

Source§

fn clone(&self) -> DAG<'a>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Default for DAG<'a>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for DAG<'a>

§

impl<'a> RefUnwindSafe for DAG<'a>

§

impl<'a> Send for DAG<'a>

§

impl<'a> Sync for DAG<'a>

§

impl<'a> Unpin for DAG<'a>

§

impl<'a> UnwindSafe for DAG<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.