You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vibed/nbd/.beans/nbd-csdh--add-graph-computa...

3.0 KiB

title status type priority created_at updated_at
Add graph computation module (src/graph.rs) completed feature normal 2026-03-10T23:30:31Z 2026-03-10T23:30:31Z

Implement src/graph.rs — a module that builds a directed dependency graph from a flat list of tickets and provides the data structures needed by the ASCII renderer and JSON output.

Motivation

The nbd graph command (see CLI ticket) needs to traverse Ticket.dependencies edges to produce an ordered, tree-structured representation of the dependency DAG. This module isolates that logic from I/O and rendering.

Data structures

/// A node in the dependency graph.
pub struct GraphNode<'a> {
    pub ticket: &'a Ticket,
    /// Direct dependents (tickets that list this ticket as a dependency).
    pub dependents: Vec<&'a str>,
    /// Direct dependencies (tickets this ticket depends on).
    pub dependencies: Vec<&'a str>,
}

/// A directed dependency graph built from a flat list of tickets.
pub struct TicketGraph<'a> {
    nodes: IndexMap<&'a str, GraphNode<'a>>,
}

Functions to implement

TicketGraph::build(tickets: &[Ticket]) -> TicketGraph

  • Constructs nodes map keyed by ticket ID.
  • Iterates ticket.dependencies to populate both dependencies (forward) and dependents (reverse) edges.
  • IDs in dependencies that do not correspond to a known ticket are silently ignored (dangling references are tolerated).

TicketGraph::roots(&self) -> Vec<&Ticket>

  • Returns tickets with no dependencies (or whose dependencies are all outside the graph).
  • Sorted by priority descending (same ordering as list_tickets).
  • These become the starting points for the recursive ASCII tree renderer.

TicketGraph::subtree(&self, root_id: &str) -> Vec<&str>

  • Returns all ticket IDs reachable from root_id by following dependencies edges in depth-first order, including root_id itself.
  • Cycles: track visited set; stop recursion when an ID has been visited. This makes the function safe even if the data contains cycles.

TicketGraph::to_json_value(&self) -> serde_json::Value

  • Returns an object like:
    {
      "nodes": [
        {"id": "a3f9c2", "title": "...", "status": "todo", "dependencies": ["b7d41e"]},
        ...
      ],
      "edges": [
        {"from": "a3f9c2", "to": "b7d41e"},
        ...
      ]
    }
    

Crate dependencies

No new crates needed. If IndexMap insertion-order is useful, indexmap can be added — but a HashMap with a separate sorted Vec<&str> of keys also works. Prefer whatever avoids adding a new crate dependency.

Files touched

  • src/graph.rs — new file, public module
  • src/main.rsmod graph; declaration

Tests (unit, in src/tests.rs)

  • build with an empty slice returns an empty graph.
  • roots returns only tickets with no in-graph dependencies.
  • subtree returns the correct set of IDs for a linear chain.
  • subtree does not infinite-loop when the data contains a cycle.
  • to_json_value contains all expected IDs in nodes and all edges in edges.