zrx_graph/graph.rs
1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Graph.
27
28use std::ops::{Index, IndexMut, Range};
29
30mod builder;
31mod error;
32pub mod iter;
33mod macros;
34pub mod operator;
35mod property;
36pub mod topology;
37pub mod traversal;
38
39pub use builder::Builder;
40pub use error::{Error, Result};
41use topology::{Direct, Topology, Transitive};
42use traversal::Traversal;
43
44// ----------------------------------------------------------------------------
45// Structs
46// ----------------------------------------------------------------------------
47
48/// Graph.
49///
50/// This data type represents a directed graph with nodes of type `T`, which is
51/// optimized for very efficient traversal, since it offers lookups of nodes and
52/// edges in O(1), i.e., constant time. It's built with the [`Graph::builder`]
53/// method, which allows to add nodes and edges, before building the graph.
54///
55/// Note that this implementation is heavily opinionated – it does not support
56/// edge weights or undirected edges, as it's built solely for our own purposes.
57/// It does by no means attempt to be a general-purpose graph library. For this,
58/// please refer to libraries such as [`petgraph`].
59///
60/// [`petgraph`]: https://docs.rs/petgraph/
61///
62/// # Examples
63///
64/// ```
65/// # use std::error::Error;
66/// # fn main() -> Result<(), Box<dyn Error>> {
67/// use zrx_graph::Graph;
68///
69/// // Create graph builder and add nodes
70/// let mut builder = Graph::builder();
71/// let a = builder.add_node("a");
72/// let b = builder.add_node("b");
73/// let c = builder.add_node("c");
74///
75/// // Create edges between nodes
76/// builder.add_edge(a, b)?;
77/// builder.add_edge(b, c)?;
78///
79/// // Create graph from builder
80/// let graph = builder.build().into_transitive();
81///
82/// // Create topological traversal
83/// let mut traversal = graph.traverse([a]);
84/// while let Some(node) = traversal.take() {
85/// println!("{node:?}");
86/// traversal.complete(node)?;
87/// }
88/// # Ok(())
89/// # }
90/// ```
91#[derive(Clone, Debug)]
92pub struct Graph<T, R = Direct> {
93 /// Graph data.
94 data: Vec<T>,
95 /// Graph topology.
96 topology: Topology<R>,
97}
98
99// ----------------------------------------------------------------------------
100// Implementations
101// ----------------------------------------------------------------------------
102
103impl<T, R> Graph<T, R> {
104 /// Creates an iterator over the graph.
105 ///
106 /// This iterator emits the node indices, which is exactly the same as
107 /// iterating over the adjacency list using `0..self.len()`.
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// # use std::error::Error;
113 /// # fn main() -> Result<(), Box<dyn Error>> {
114 /// use zrx_graph::topology::Adjacency;
115 /// use zrx_graph::Graph;
116 ///
117 /// // Create graph builder and add nodes
118 /// let mut builder = Graph::builder();
119 /// let a = builder.add_node("a");
120 /// let b = builder.add_node("b");
121 /// let c = builder.add_node("c");
122 ///
123 /// // Create edges between nodes
124 /// builder.add_edge(a, b)?;
125 /// builder.add_edge(b, c)?;
126 ///
127 /// // Create graph from builder
128 /// let graph = builder.build();
129 ///
130 /// // Create iterator over graph
131 /// for node in graph.iter() {
132 /// println!("{node:?}");
133 /// }
134 /// # Ok(())
135 /// # }
136 /// ```
137 #[inline]
138 #[must_use]
139 pub fn iter(&self) -> Range<usize> {
140 0..self.data.len()
141 }
142}
143
144impl<T> Graph<T, Direct> {
145 /// Converts this graph into one with transitive reachability.
146 ///
147 /// # Examples
148 ///
149 /// ```
150 /// # use std::error::Error;
151 /// # fn main() -> Result<(), Box<dyn Error>> {
152 /// use zrx_graph::Graph;
153 ///
154 /// // Create graph builder and add nodes
155 /// let mut builder = Graph::builder();
156 /// let a = builder.add_node("a");
157 /// let b = builder.add_node("b");
158 /// let c = builder.add_node("c");
159 ///
160 /// // Create edges between nodes
161 /// builder.add_edge(a, b)?;
162 /// builder.add_edge(b, c)?;
163 ///
164 /// // Create graph from builder
165 /// let graph = builder.build().into_transitive();
166 /// # Ok(())
167 /// # }
168 /// ```
169 #[must_use]
170 pub fn into_transitive(self) -> Graph<T, Transitive> {
171 Graph {
172 data: self.data,
173 topology: self.topology.into_transitive(),
174 }
175 }
176}
177
178impl<T> Graph<T, Transitive> {
179 /// Creates a topogical traversal starting from the given initial nodes.
180 ///
181 /// This method creates a topological traversal of the graph, which allows
182 /// to visit nodes in a topological order, i.e., visiting a node only after
183 /// all of its dependencies have been visited. The traversal is initialized
184 /// with the given initial nodes, which are the starting points.
185 ///
186 /// Note that an arbitrary number of parallel traversals can be created
187 /// from the same graph, as the underlying topology is shared between them.
188 ///
189 /// # Examples
190 ///
191 /// ```
192 /// # use std::error::Error;
193 /// # fn main() -> Result<(), Box<dyn Error>> {
194 /// use zrx_graph::Graph;
195 ///
196 /// // Create graph builder and add nodes
197 /// let mut builder = Graph::builder();
198 /// let a = builder.add_node("a");
199 /// let b = builder.add_node("b");
200 /// let c = builder.add_node("c");
201 ///
202 /// // Create edges between nodes
203 /// builder.add_edge(a, b)?;
204 /// builder.add_edge(b, c)?;
205 ///
206 /// // Create graph from builder
207 /// let graph = builder.build().into_transitive();
208 ///
209 /// // Create topological traversal
210 /// let mut traversal = graph.traverse([a]);
211 /// while let Some(node) = traversal.take() {
212 /// println!("{node:?}");
213 /// traversal.complete(node)?;
214 /// }
215 /// # Ok(())
216 /// # }
217 /// ```
218 #[inline]
219 pub fn traverse<I>(&self, initial: I) -> Traversal
220 where
221 I: AsRef<[usize]>,
222 {
223 Traversal::new(&self.topology, initial)
224 }
225}
226
227#[allow(clippy::must_use_candidate)]
228impl<T, R> Graph<T, R> {
229 /// Returns a reference to the graph topology.
230 #[inline]
231 pub fn topology(&self) -> &Topology<R> {
232 &self.topology
233 }
234
235 /// Returns the number of nodes.
236 #[inline]
237 pub fn len(&self) -> usize {
238 self.data.len()
239 }
240
241 /// Returns whether there are any nodes.
242 #[inline]
243 pub fn is_empty(&self) -> bool {
244 self.data.is_empty()
245 }
246}
247
248// ----------------------------------------------------------------------------
249// Trait implementations
250// ----------------------------------------------------------------------------
251
252impl<T, R> AsRef<[T]> for Graph<T, R> {
253 /// Returns the graph data as a slice.
254 #[inline]
255 fn as_ref(&self) -> &[T] {
256 &self.data
257 }
258}
259
260// ----------------------------------------------------------------------------
261
262impl<T, R> Index<usize> for Graph<T, R> {
263 type Output = T;
264
265 /// Returns a reference to the node at the index.
266 ///
267 /// # Panics
268 ///
269 /// Panics if the index is out of bounds.
270 ///
271 /// # Examples
272 ///
273 /// ```
274 /// # use std::error::Error;
275 /// # fn main() -> Result<(), Box<dyn Error>> {
276 /// use zrx_graph::topology::Adjacency;
277 /// use zrx_graph::Graph;
278 ///
279 /// // Create graph builder and add nodes
280 /// let mut builder = Graph::builder();
281 /// let a = builder.add_node("a");
282 /// let b = builder.add_node("b");
283 /// let c = builder.add_node("c");
284 ///
285 /// // Create edges between nodes
286 /// builder.add_edge(a, b)?;
287 /// builder.add_edge(b, c)?;
288 ///
289 /// // Create graph from builder
290 /// let graph = builder.build();
291 ///
292 /// // Obtain references to nodes
293 /// assert_eq!(&graph[a], &"a");
294 /// assert_eq!(&graph[b], &"b");
295 /// assert_eq!(&graph[c], &"c");
296 /// # Ok(())
297 /// # }
298 /// ```
299 #[inline]
300 fn index(&self, index: usize) -> &Self::Output {
301 &self.data[index]
302 }
303}
304
305impl<T, R> IndexMut<usize> for Graph<T, R> {
306 /// Returns a mutable reference to the node at the index.
307 ///
308 /// # Panics
309 ///
310 /// Panics if the index is out of bounds.
311 ///
312 /// # Examples
313 ///
314 /// ```
315 /// # use std::error::Error;
316 /// # fn main() -> Result<(), Box<dyn Error>> {
317 /// use zrx_graph::topology::Adjacency;
318 /// use zrx_graph::Graph;
319 ///
320 /// // Create graph builder and add nodes
321 /// let mut builder = Graph::builder();
322 /// let a = builder.add_node("a");
323 /// let b = builder.add_node("b");
324 /// let c = builder.add_node("c");
325 ///
326 /// // Create edges between nodes
327 /// builder.add_edge(a, b)?;
328 /// builder.add_edge(b, c)?;
329 ///
330 /// // Create graph from builder
331 /// let mut graph = builder.build();
332 ///
333 /// // Obtain mutable references to nodes
334 /// assert_eq!(&mut graph[a], &mut "a");
335 /// assert_eq!(&mut graph[b], &mut "b");
336 /// assert_eq!(&mut graph[c], &mut "c");
337 /// # Ok(())
338 /// # }
339 /// ```
340 #[inline]
341 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
342 &mut self.data[index]
343 }
344}
345
346// ----------------------------------------------------------------------------
347
348impl<T> From<Builder<T>> for Graph<T> {
349 /// Creates a graph from a builder.
350 ///
351 /// # Examples
352 ///
353 /// ```
354 /// # use std::error::Error;
355 /// # fn main() -> Result<(), Box<dyn Error>> {
356 /// use zrx_graph::Graph;
357 ///
358 /// // Create graph builder and add nodes
359 /// let mut builder = Graph::builder();
360 /// let a = builder.add_node("a");
361 /// let b = builder.add_node("b");
362 /// let c = builder.add_node("c");
363 ///
364 /// // Create edges between nodes
365 /// builder.add_edge(a, b)?;
366 /// builder.add_edge(b, c)?;
367 ///
368 /// // Create graph from builder
369 /// let graph = Graph::from(builder);
370 /// # Ok(())
371 /// # }
372 /// ```
373 #[inline]
374 fn from(builder: Builder<T>) -> Self {
375 builder.build()
376 }
377}
378
379// ----------------------------------------------------------------------------
380
381impl<T, R> IntoIterator for &Graph<T, R> {
382 type Item = usize;
383 type IntoIter = Range<usize>;
384
385 /// Creates an iterator over the graph.
386 ///
387 /// This iterator emits the node indices, which is exactly the same as
388 /// iterating over the adjacency list using `0..self.len()`.
389 ///
390 /// # Examples
391 ///
392 /// ```
393 /// # use std::error::Error;
394 /// # fn main() -> Result<(), Box<dyn Error>> {
395 /// use zrx_graph::topology::Adjacency;
396 /// use zrx_graph::Graph;
397 ///
398 /// // Create graph builder and add nodes
399 /// let mut builder = Graph::builder();
400 /// let a = builder.add_node("a");
401 /// let b = builder.add_node("b");
402 /// let c = builder.add_node("c");
403 ///
404 /// // Create edges between nodes
405 /// builder.add_edge(a, b)?;
406 /// builder.add_edge(b, c)?;
407 ///
408 /// // Create graph from builder
409 /// let graph = builder.build();
410 ///
411 /// // Create iterator over graph
412 /// for node in &graph {
413 /// println!("{node:?}");
414 /// }
415 /// # Ok(())
416 /// # }
417 /// ```
418 #[inline]
419 fn into_iter(self) -> Self::IntoIter {
420 self.iter()
421 }
422}
423
424// ----------------------------------------------------------------------------
425
426impl<T> Default for Graph<T> {
427 /// Creates an empty graph.
428 ///
429 /// While an empty graph is not very useful, it's sometimes practical as a
430 /// placeholder in documentation, and in types implementing [`Default`].
431 ///
432 /// # Examples
433 ///
434 /// ```
435 /// use zrx_graph::Graph;
436 ///
437 /// // Create empty graph
438 /// let graph = Graph::default();
439 /// # let _: Graph<()> = graph;
440 /// assert!(graph.is_empty());
441 /// ```
442 #[inline]
443 fn default() -> Self {
444 Self::builder().build()
445 }
446}