pub struct Graph {
pub direction: Direction,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
pub subgraphs: Vec<Subgraph>,
pub node_styles: HashMap<String, NodeStyle>,
pub edge_styles: HashMap<usize, EdgeStyleColors>,
pub class_defs: HashMap<String, NodeStyle>,
pub subgraph_styles: HashMap<String, NodeStyle>,
pub click_targets: HashMap<String, ClickTarget>,
}Expand description
A parsed flowchart graph ready for layout and rendering.
Fields§
§direction: DirectionThe overall flow direction.
nodes: Vec<Node>All nodes in declaration order.
edges: Vec<Edge>All edges in declaration order.
subgraphs: Vec<Subgraph>All top-level subgraphs in declaration order.
Subgraphs may nest: a subgraph’s subgraph_ids list references the
IDs of its immediate children. Use Graph::node_to_subgraph for
efficient node→subgraph lookups.
node_styles: HashMap<String, NodeStyle>Per-node color overrides parsed from style <id> ... directives.
Empty by default; populated only when the source contains style
directives. Used by the renderer when ANSI color output is enabled.
edge_styles: HashMap<usize, EdgeStyleColors>Per-edge color overrides parsed from linkStyle <index> ... directives.
Keyed by the edge’s positional index (0-based, in declaration order). Empty by default.
class_defs: HashMap<String, NodeStyle>Named style classes from classDef name fill:#…,stroke:#…,color:#…
directives. Acts as the palette that class A foo and A:::foo
look up at end-of-parse to populate node_styles /
subgraph_styles. Multiple classDef entries with the same name
are last-wins (matches Mermaid).
subgraph_styles: HashMap<String, NodeStyle>Per-subgraph color overrides — populated when class CompositeId styleName is applied to a known composite/subgraph id. The
renderer paints the rounded border with stroke. fill and
color are accepted in the schema for consistency with
node_styles but only stroke is honoured today (filling a
composite’s interior would conflict with inner node fills).
click_targets: HashMap<String, ClickTarget>Hyperlink targets from click NodeId "url" directives.
Keyed by node ID; present only for nodes that have an explicit
click directive with a URL. JS-callback forms (click NodeId callbackFn) are silently ignored.
Used by the renderer to wrap node labels in OSC 8 hyperlink escape sequences when emitting Unicode output.
Implementations§
Source§impl Graph
impl Graph
Sourcepub fn new(direction: Direction) -> Self
pub fn new(direction: Direction) -> Self
Construct a new empty graph with the given direction.
§Arguments
direction— the overall flow direction for this graph
§Examples
use mermaid_text::{Graph, Direction};
let g = Graph::new(Direction::LeftToRight);
assert_eq!(g.direction, Direction::LeftToRight);
assert!(g.nodes.is_empty());
assert!(g.edges.is_empty());Sourcepub fn node(&self, id: &str) -> Option<&Node>
pub fn node(&self, id: &str) -> Option<&Node>
Look up a node by its ID, returning a reference if found.
§Arguments
id— the node identifier to search for
§Examples
use mermaid_text::{Graph, Node, NodeShape, Direction};
let mut g = Graph::new(Direction::LeftToRight);
g.nodes.push(Node::new("A", "Start", NodeShape::Rectangle));
assert_eq!(g.node("A").map(|n| n.label.as_str()), Some("Start"));
assert!(g.node("Z").is_none());Sourcepub fn has_node(&self, id: &str) -> bool
pub fn has_node(&self, id: &str) -> bool
Return true if a node with id already exists.
§Examples
use mermaid_text::{Graph, Node, NodeShape, Direction};
let mut g = Graph::new(Direction::TopToBottom);
g.nodes.push(Node::new("A", "A", NodeShape::Rectangle));
assert!(g.has_node("A"));
assert!(!g.has_node("B"));Sourcepub fn upsert_node(&mut self, node: Node)
pub fn upsert_node(&mut self, node: Node)
Insert a node, or update its label/shape if the ID already exists and the existing entry was auto-created as a bare-id placeholder.
A “bare-id placeholder” is a node whose label == id and shape == Rectangle
(the default produced when a node is first seen in an edge definition
without an explicit shape). If such a placeholder already exists and the
incoming node has a richer definition (different label or non-default shape),
the placeholder is promoted to the richer definition.
§Examples
use mermaid_text::{Graph, Node, NodeShape, Direction};
let mut g = Graph::new(Direction::LeftToRight);
// Insert a bare-id placeholder.
g.upsert_node(Node::new("A", "A", NodeShape::Rectangle));
// Promote it to a richer definition.
g.upsert_node(Node::new("A", "Start", NodeShape::Rounded));
assert_eq!(g.node("A").unwrap().label, "Start");
assert_eq!(g.node("A").unwrap().shape, NodeShape::Rounded);
// If neither condition holds, the existing entry is kept.
g.upsert_node(Node::new("A", "Other", NodeShape::Diamond));
assert_eq!(g.node("A").unwrap().label, "Start"); // unchangedSourcepub fn node_to_subgraph(&self) -> HashMap<String, String>
pub fn node_to_subgraph(&self) -> HashMap<String, String>
Build a flat map from node ID → the ID of the innermost subgraph
that contains it (only direct node_ids members, not transitive).
The map is computed on demand and not cached — call this once per render pass and keep the result locally.
§Examples
let graph = mermaid_text::parser::parse(
"graph LR\nsubgraph S\nA-->B\nend\nC",
).unwrap();
let map = graph.node_to_subgraph();
assert_eq!(map.get("A").map(String::as_str), Some("S"));
assert_eq!(map.get("B").map(String::as_str), Some("S"));
assert!(map.get("C").is_none());Sourcepub fn find_subgraph(&self, id: &str) -> Option<&Subgraph>
pub fn find_subgraph(&self, id: &str) -> Option<&Subgraph>
Find a subgraph by ID, searching recursively through all nesting levels.
§Arguments
id— the subgraph identifier to search for
§Examples
let graph = mermaid_text::parser::parse(
"graph TD\nsubgraph Outer\nsubgraph Inner\nA\nend\nend",
).unwrap();
assert!(graph.find_subgraph("Outer").is_some());
assert!(graph.find_subgraph("Inner").is_some());
assert!(graph.find_subgraph("Missing").is_none());Sourcepub fn all_nodes_in_subgraph(&self, sg: &Subgraph) -> Vec<String>
pub fn all_nodes_in_subgraph(&self, sg: &Subgraph) -> Vec<String>
Collect all node IDs that belong to sg or any of its nested subgraphs.
This is a deep traversal: nodes in nested subgraphs within sg are
included in the result, not just direct sg.node_ids members.
§Arguments
sg— the subgraph to collect nodes from (including descendants)
§Examples
let graph = mermaid_text::parser::parse(
"graph TD\nsubgraph Outer\nsubgraph Inner\nA\nend\nB\nend",
).unwrap();
let outer = graph.find_subgraph("Outer").unwrap();
let nodes = graph.all_nodes_in_subgraph(outer);
assert!(nodes.contains(&"A".to_string()));
assert!(nodes.contains(&"B".to_string()));Sourcepub fn parallel_edge_groups(&self) -> Vec<Vec<usize>>
pub fn parallel_edge_groups(&self) -> Vec<Vec<usize>>
Group edge indices by their unordered endpoint pair, returning only the groups containing more than one edge.
Two edges are “parallel” iff they share the same unordered
(from, to) endpoints — so F → W and W → F belong to the
same group, as do T ==>|pass| D and T -.->|skip| D.
Self-loops (A → A) are kept as singleton groups; they’re
included in the output only when an entity has multiple
self-loops, which is rare but possible.
Used by the renderer’s parallel-channel allocation pass
(Phase 2 of the layout-pass widening work — see
docs/scope-parallel-edges.md) to give each edge in a group
its own row (LR) or column (TD) so labels stack cleanly
instead of competing for one inter-layer cell.
Returns Vec<Vec<usize>> where each inner Vec contains edge
indices in source order (edges at lower index render first).
Each inner Vec has length ≥ 2; non-parallel edges are absent
from the output entirely.