weavatrix_graph/graph/
core.rs1use super::bulk;
2use crate::{
3 Edge, EdgeEndpoints, EdgeIndex, GraphView, IndexGraphView, Node, NodeId, NodeIndex, Result,
4 Topology,
5};
6use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
7
8pub type GraphNodeIndex = NodeIndex;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
12pub struct Graph {
13 nodes: Vec<Node>,
14 edges: Vec<Edge>,
15 #[serde(skip)]
16 topology: Topology,
17}
18
19impl Graph {
20 pub(super) const fn from_indexed_parts(
21 nodes: Vec<Node>,
22 edges: Vec<Edge>,
23 topology: Topology,
24 ) -> Self {
25 Self {
26 nodes,
27 edges,
28 topology,
29 }
30 }
31
32 pub(crate) fn from_validated_sorted_parts(nodes: Vec<Node>, edges: Vec<Edge>) -> Result<Self> {
33 let topology = super::index::index_canonical_edges(&nodes, &edges)?;
34 Ok(Self::from_indexed_parts(nodes, edges, topology))
35 }
36
37 pub fn try_from_parts(
44 nodes: impl IntoIterator<Item = Node>,
45 edges: impl IntoIterator<Item = Edge>,
46 ) -> Result<Self> {
47 bulk::canonical(nodes, edges)
48 }
49
50 pub fn try_from_sorted_nodes(
59 nodes: Vec<Node>,
60 edges: impl IntoIterator<Item = Edge>,
61 ) -> Result<Self> {
62 bulk::from_sorted_nodes(nodes, edges)
63 }
64
65 pub fn try_from_sorted_parts(nodes: Vec<Node>, edges: Vec<Edge>) -> Result<Self> {
73 bulk::from_sorted_parts(nodes, edges)
74 }
75
76 #[must_use]
77 pub fn nodes(&self) -> &[Node] {
78 &self.nodes
79 }
80
81 #[must_use]
82 pub fn edges(&self) -> &[Edge] {
83 &self.edges
84 }
85
86 #[must_use]
87 pub const fn topology(&self) -> &Topology {
88 &self.topology
89 }
90
91 #[must_use]
92 pub fn node(&self, id: &str) -> Option<&Node> {
93 self.node_index(id).and_then(|index| self.node_at(index))
94 }
95
96 pub fn outgoing<'graph>(&'graph self, id: &NodeId) -> impl Iterator<Item = &'graph Edge> {
97 self.node_index(id.as_str())
98 .into_iter()
99 .flat_map(|index| self.topology.outgoing_edges(index))
100 .map(|edge| &self.edges[edge.index()])
101 }
102
103 pub fn incoming<'graph>(&'graph self, id: &NodeId) -> impl Iterator<Item = &'graph Edge> {
104 self.node_index(id.as_str())
105 .into_iter()
106 .flat_map(|index| self.topology.incoming_edges(index))
107 .map(|edge| &self.edges[edge.index()])
108 }
109
110 #[must_use]
111 pub fn edge_at(&self, index: EdgeIndex) -> Option<&Edge> {
112 self.edges.get(index.index())
113 }
114
115 #[must_use]
116 pub fn node_index(&self, id: &str) -> Option<GraphNodeIndex> {
117 self.nodes
118 .binary_search_by(|node| node.id.as_str().cmp(id))
119 .ok()
120 .and_then(|index| u32::try_from(index).ok())
121 .map(NodeIndex::new)
122 }
123
124 #[must_use]
125 pub fn node_at(&self, index: GraphNodeIndex) -> Option<&Node> {
126 self.nodes.get(index.index())
127 }
128
129 pub fn outgoing_at(&self, index: GraphNodeIndex) -> impl Iterator<Item = &Edge> {
130 self.topology
131 .outgoing_edges(index)
132 .map(|edge| &self.edges[edge.index()])
133 }
134
135 pub fn incoming_at(&self, index: GraphNodeIndex) -> impl Iterator<Item = &Edge> {
136 self.topology
137 .incoming_edges(index)
138 .map(|edge| &self.edges[edge.index()])
139 }
140
141 pub fn outgoing_neighbors_at(
142 &self,
143 index: GraphNodeIndex,
144 ) -> impl Iterator<Item = GraphNodeIndex> + '_ {
145 self.topology.outgoing_neighbors(index)
146 }
147
148 pub fn incoming_neighbors_at(
149 &self,
150 index: GraphNodeIndex,
151 ) -> impl Iterator<Item = GraphNodeIndex> + '_ {
152 self.topology.incoming_neighbors(index)
153 }
154
155 #[must_use]
156 pub fn out_degree(&self, index: GraphNodeIndex) -> Option<usize> {
157 self.topology.out_degree(index)
158 }
159
160 #[must_use]
161 pub fn in_degree(&self, index: GraphNodeIndex) -> Option<usize> {
162 self.topology.in_degree(index)
163 }
164
165 #[must_use]
166 pub const fn node_count(&self) -> usize {
167 self.nodes.len()
168 }
169
170 #[must_use]
171 pub const fn edge_count(&self) -> usize {
172 self.edges.len()
173 }
174
175 #[must_use]
176 pub const fn is_empty(&self) -> bool {
177 self.nodes.is_empty() && self.edges.is_empty()
178 }
179
180 #[must_use]
181 pub fn into_parts(self) -> (Vec<Node>, Vec<Edge>) {
182 (self.nodes, self.edges)
183 }
184}
185
186impl GraphView for Graph {
187 type Node = NodeIndex;
188 type Edge = EdgeIndex;
189
190 fn node_count(&self) -> usize {
191 self.node_count()
192 }
193
194 fn edge_count(&self) -> usize {
195 self.edge_count()
196 }
197
198 fn contains_node(&self, node: NodeIndex) -> bool {
199 self.node_at(node).is_some()
200 }
201
202 fn contains_edge(&self, edge: EdgeIndex) -> bool {
203 self.edge_at(edge).is_some()
204 }
205
206 fn node_indices(&self) -> impl Iterator<Item = Self::Node> + '_ {
207 self.topology.node_indices()
208 }
209
210 fn edge_indices(&self) -> impl Iterator<Item = Self::Edge> + '_ {
211 self.topology.edge_indices()
212 }
213
214 fn edge_endpoints(&self, edge: EdgeIndex) -> Option<EdgeEndpoints> {
215 self.topology.edge_endpoints(edge)
216 }
217
218 fn outgoing_edges(
219 &self,
220 node: NodeIndex,
221 ) -> impl DoubleEndedIterator<Item = EdgeIndex> + ExactSizeIterator + '_ {
222 self.topology.outgoing_edges(node)
223 }
224
225 fn incoming_edges(
226 &self,
227 node: NodeIndex,
228 ) -> impl DoubleEndedIterator<Item = EdgeIndex> + ExactSizeIterator + '_ {
229 self.topology.incoming_edges(node)
230 }
231}
232
233impl IndexGraphView for Graph {
234 fn node_bound(&self) -> usize {
235 self.topology.node_bound()
236 }
237
238 fn edge_bound(&self) -> usize {
239 self.topology.edge_bound()
240 }
241
242 fn node_slot(node: Self::Node) -> usize {
243 node.index()
244 }
245
246 fn edge_slot(edge: Self::Edge) -> usize {
247 edge.index()
248 }
249}
250
251#[derive(Deserialize)]
252struct GraphWire {
253 nodes: Vec<Node>,
254 edges: Vec<Edge>,
255}
256
257impl<'de> Deserialize<'de> for Graph {
258 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
259 where
260 D: Deserializer<'de>,
261 {
262 let wire = GraphWire::deserialize(deserializer)?;
263 Self::try_from_parts(wire.nodes, wire.edges).map_err(D::Error::custom)
264 }
265}