+++ title = "ASCII graph rendering in display.rs" priority = 5 status = "done" ticket_type = "feature" dependencies = ["9c9ebe"] +++ Add `format_graph` and `print_graph` to `src/display.rs` to render a ticket dependency DAG as an ASCII tree. ## Motivation The `nbd graph` command needs to convert the `TicketGraph` data structure (from `src/graph.rs`) into a human-readable ASCII representation. Rendering belongs in `display.rs` next to `format_list` and `format_ticket`. ## Output format The graph is a forest of trees. Each root ticket (no in-graph dependencies) starts at column 0. Its dependents (tickets that depend on it) are indented below it using box-drawing characters. ``` a3f9c2 [todo] Fix login bug ├── b7d41e [todo] Add rate limiting │ └── c9e823 [in_progress] Write tests └── d1f302 [done] Update docs e4a781 [todo] New feature (no deps) ``` **Node format per line:** ``` {prefix}{id} [{status}] {title} ``` Where `{prefix}` is built from indentation characters (`│ `, `├── `, `└── `). **Blocked indicator (optional):** Consider marking tickets that have unresolved (non-done/closed) dependencies with a `[blocked]` tag or `!` prefix so the graph visually distinguishes ready from blocked tickets. ## Signatures ```rust /// Render the full dependency forest as an ASCII tree string. pub fn format_graph(graph: &TicketGraph) -> String /// Print the full dependency forest. pub fn print_graph(graph: &TicketGraph) /// Render the subtree rooted at `root_id` as an ASCII tree string. pub fn format_subtree(graph: &TicketGraph, root_id: &str) -> String /// Print the subtree rooted at `root_id`. pub fn print_subtree(graph: &TicketGraph, root_id: &str) ``` ## Implementation notes - Use a recursive helper that tracks a `prefix: String` carrying the accumulated indentation characters. - For each node's children (its dependents in the graph), iterate: - If not the last child: prefix extension is `│ `; connector is `├── `. - If the last child: prefix extension is ` `; connector is `└── `. - Cycle guard: track a `visited: HashSet<&str>` across the recursion; if a node ID is already visited, render `{prefix}{connector}{id} [cycle]` and stop descending. - Status is shown as the serde string: `todo`, `in_progress`, `done`, `closed`. ## Files touched - `src/display.rs` — `format_graph`, `print_graph`, `format_subtree`, `print_subtree` ## Tests (unit, in `src/tests.rs`) - `format_graph` on a single ticket with no deps produces a single line. - `format_graph` on a two-ticket chain shows the child indented with `└──`. - `format_graph` with a branching parent shows `├──` for all but the last child and `└──` for the last. - `format_subtree` only shows the specified root's subtree. - A cycle in the graph does not cause infinite recursion; the repeated node is labelled `[cycle]`.