Skip to main content

openquiver_graph/
graph.rs

1//! The graph itself.
2
3use std::collections::{BTreeMap, BTreeSet};
4
5use serde::{Deserialize, Serialize};
6
7use crate::{Edge, Node, NodeName, PinRef};
8
9/// A graph: nodes joined by exec and data wires.
10///
11/// Nodes live in an ordered map rather than a vector because the canonical
12/// order is alphabetical by name. That order is what keeps diffs stable and
13/// spreads concurrent insertions by different people across the file instead
14/// of crowding them at the end.
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub struct Graph {
17    name: String,
18    pub(crate) nodes: BTreeMap<NodeName, Node>,
19    pub(crate) edges: BTreeSet<Edge>,
20}
21
22impl Graph {
23    pub fn new(name: impl Into<String>) -> Self {
24        Self {
25            name: name.into(),
26            nodes: BTreeMap::new(),
27            edges: BTreeSet::new(),
28        }
29    }
30
31    pub fn name(&self) -> &str {
32        &self.name
33    }
34
35    pub fn insert_node(&mut self, node: Node) {
36        self.nodes.insert(node.name.clone(), node);
37    }
38
39    pub fn node(&self, name: &str) -> Option<&Node> {
40        // ASCII is already normalized, so the overwhelmingly common case is a
41        // borrowed probe with no allocation. Only a name that might decompose
42        // pays for normalizing.
43        if name.is_ascii() {
44            return self.nodes.get(name);
45        }
46        self.nodes.get(&NodeName::new(name))
47    }
48
49    /// Nodes in canonical order, by name.
50    pub fn nodes(&self) -> impl Iterator<Item = &Node> {
51        self.nodes.values()
52    }
53
54    /// A set rather than a vector: the same wire drawn twice, including by two
55    /// people on separate branches, stays a single edge.
56    pub fn insert_edge(&mut self, edge: Edge) {
57        self.edges.insert(edge);
58    }
59
60    /// Edges in canonical order: exec first, then data, each sorted by its pin
61    /// pair.
62    pub fn edges(&self) -> impl Iterator<Item = &Edge> {
63        self.edges.iter()
64    }
65
66    /// Wires leaving a pin.
67    ///
68    /// The editor needs this for Blueprint's rule that connecting a second wire
69    /// to an exec output replaces the first, and the compiler needs it to check
70    /// pin cardinality at all.
71    pub fn edges_from(&self, pin: &PinRef) -> impl Iterator<Item = &Edge> {
72        self.edges.iter().filter(move |e| e.from() == pin)
73    }
74
75    /// Wires arriving at a pin.
76    pub fn edges_into(&self, pin: &PinRef) -> impl Iterator<Item = &Edge> {
77        self.edges.iter().filter(move |e| e.to() == pin)
78    }
79}