Skip to main content

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::Topology;
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();
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> {
93    /// Graph data.
94    data: Vec<T>,
95    /// Graph topology.
96    topology: Topology,
97}
98
99// ----------------------------------------------------------------------------
100// Implementations
101// ----------------------------------------------------------------------------
102
103impl<T> Graph<T> {
104    /// Creates a topogical traversal starting from the given initial nodes.
105    ///
106    /// This method creates a topological traversal of the graph, which allows
107    /// to visit nodes in a topological order, i.e., visiting a node only after
108    /// all of its dependencies have been visited. The traversal is initialized
109    /// with the given initial nodes, which are the starting points.
110    ///
111    /// Note that an arbitrary number of parallel traversals can be created
112    /// from the same graph, as the underlying topology is shared between them.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// # use std::error::Error;
118    /// # fn main() -> Result<(), Box<dyn Error>> {
119    /// use zrx_graph::Graph;
120    ///
121    /// // Create graph builder and add nodes
122    /// let mut builder = Graph::builder();
123    /// let a = builder.add_node("a");
124    /// let b = builder.add_node("b");
125    /// let c = builder.add_node("c");
126    ///
127    /// // Create edges between nodes
128    /// builder.add_edge(a, b)?;
129    /// builder.add_edge(b, c)?;
130    ///
131    /// // Create graph from builder
132    /// let graph = builder.build();
133    ///
134    /// // Create topological traversal
135    /// let mut traversal = graph.traverse([a]);
136    /// while let Some(node) = traversal.take() {
137    ///     println!("{node:?}");
138    ///     traversal.complete(node)?;
139    /// }
140    /// # Ok(())
141    /// # }
142    /// ```
143    #[inline]
144    pub fn traverse<I>(&self, initial: I) -> Traversal
145    where
146        I: AsRef<[usize]>,
147    {
148        Traversal::new(&self.topology, initial)
149    }
150
151    /// Creates an iterator over the graph.
152    ///
153    /// This iterator emits the node indices, which is exactly the same as
154    /// iterating over the adjacency list using `0..self.len()`.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// # use std::error::Error;
160    /// # fn main() -> Result<(), Box<dyn Error>> {
161    /// use zrx_graph::topology::Adjacency;
162    /// use zrx_graph::Graph;
163    ///
164    /// // Create graph builder and add nodes
165    /// let mut builder = Graph::builder();
166    /// let a = builder.add_node("a");
167    /// let b = builder.add_node("b");
168    /// let c = builder.add_node("c");
169    ///
170    /// // Create edges between nodes
171    /// builder.add_edge(a, b)?;
172    /// builder.add_edge(b, c)?;
173    ///
174    /// // Create graph from builder
175    /// let graph = builder.build();
176    ///
177    /// // Create iterator over graph
178    /// for node in graph.iter() {
179    ///     println!("{node:?}");
180    /// }
181    /// # Ok(())
182    /// # }
183    /// ```
184    #[inline]
185    #[must_use]
186    pub fn iter(&self) -> Range<usize> {
187        0..self.data.len()
188    }
189}
190
191#[allow(clippy::must_use_candidate)]
192impl<T> Graph<T> {
193    /// Returns a reference to the graph topology.
194    #[inline]
195    pub fn topology(&self) -> &Topology {
196        &self.topology
197    }
198
199    /// Returns the number of nodes.
200    #[inline]
201    pub fn len(&self) -> usize {
202        self.data.len()
203    }
204
205    /// Returns whether there are any nodes.
206    #[inline]
207    pub fn is_empty(&self) -> bool {
208        self.data.is_empty()
209    }
210}
211
212// ----------------------------------------------------------------------------
213// Trait implementations
214// ----------------------------------------------------------------------------
215
216impl<T> AsRef<[T]> for Graph<T> {
217    /// Returns the graph data as a slice.
218    #[inline]
219    fn as_ref(&self) -> &[T] {
220        &self.data
221    }
222}
223
224// ----------------------------------------------------------------------------
225
226impl<T> Index<usize> for Graph<T> {
227    type Output = T;
228
229    /// Returns a reference to the node at the index.
230    ///
231    /// # Panics
232    ///
233    /// Panics if the index is out of bounds.
234    ///
235    /// # Examples
236    ///
237    /// ```
238    /// # use std::error::Error;
239    /// # fn main() -> Result<(), Box<dyn Error>> {
240    /// use zrx_graph::topology::Adjacency;
241    /// use zrx_graph::Graph;
242    ///
243    /// // Create graph builder and add nodes
244    /// let mut builder = Graph::builder();
245    /// let a = builder.add_node("a");
246    /// let b = builder.add_node("b");
247    /// let c = builder.add_node("c");
248    ///
249    /// // Create edges between nodes
250    /// builder.add_edge(a, b)?;
251    /// builder.add_edge(b, c)?;
252    ///
253    /// // Create graph from builder
254    /// let graph = builder.build();
255    ///
256    /// // Obtain references to nodes
257    /// assert_eq!(&graph[a], &"a");
258    /// assert_eq!(&graph[b], &"b");
259    /// assert_eq!(&graph[c], &"c");
260    /// # Ok(())
261    /// # }
262    /// ```
263    #[inline]
264    fn index(&self, index: usize) -> &Self::Output {
265        &self.data[index]
266    }
267}
268
269impl<T> IndexMut<usize> for Graph<T> {
270    /// Returns a mutable reference to the node at the index.
271    ///
272    /// # Panics
273    ///
274    /// Panics if the index is out of bounds.
275    ///
276    /// # Examples
277    ///
278    /// ```
279    /// # use std::error::Error;
280    /// # fn main() -> Result<(), Box<dyn Error>> {
281    /// use zrx_graph::topology::Adjacency;
282    /// use zrx_graph::Graph;
283    ///
284    /// // Create graph builder and add nodes
285    /// let mut builder = Graph::builder();
286    /// let a = builder.add_node("a");
287    /// let b = builder.add_node("b");
288    /// let c = builder.add_node("c");
289    ///
290    /// // Create edges between nodes
291    /// builder.add_edge(a, b)?;
292    /// builder.add_edge(b, c)?;
293    ///
294    /// // Create graph from builder
295    /// let mut graph = builder.build();
296    ///
297    /// // Obtain mutable references to nodes
298    /// assert_eq!(&mut graph[a], &mut "a");
299    /// assert_eq!(&mut graph[b], &mut "b");
300    /// assert_eq!(&mut graph[c], &mut "c");
301    /// # Ok(())
302    /// # }
303    /// ```
304    #[inline]
305    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
306        &mut self.data[index]
307    }
308}
309
310// ----------------------------------------------------------------------------
311
312impl<T> From<Builder<T>> for Graph<T> {
313    /// Creates a graph from a builder.
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// # use std::error::Error;
319    /// # fn main() -> Result<(), Box<dyn Error>> {
320    /// use zrx_graph::Graph;
321    ///
322    /// // Create graph builder and add nodes
323    /// let mut builder = Graph::builder();
324    /// let a = builder.add_node("a");
325    /// let b = builder.add_node("b");
326    /// let c = builder.add_node("c");
327    ///
328    /// // Create edges between nodes
329    /// builder.add_edge(a, b)?;
330    /// builder.add_edge(b, c)?;
331    ///
332    /// // Create graph from builder
333    /// let graph = Graph::from(builder);
334    /// # Ok(())
335    /// # }
336    /// ```
337    #[inline]
338    fn from(builder: Builder<T>) -> Self {
339        builder.build()
340    }
341}
342
343// ----------------------------------------------------------------------------
344
345impl<T> IntoIterator for &Graph<T> {
346    type Item = usize;
347    type IntoIter = Range<usize>;
348
349    /// Creates an iterator over the graph.
350    ///
351    /// This iterator emits the node indices, which is exactly the same as
352    /// iterating over the adjacency list using `0..self.len()`.
353    ///
354    /// # Examples
355    ///
356    /// ```
357    /// # use std::error::Error;
358    /// # fn main() -> Result<(), Box<dyn Error>> {
359    /// use zrx_graph::topology::Adjacency;
360    /// use zrx_graph::Graph;
361    ///
362    /// // Create graph builder and add nodes
363    /// let mut builder = Graph::builder();
364    /// let a = builder.add_node("a");
365    /// let b = builder.add_node("b");
366    /// let c = builder.add_node("c");
367    ///
368    /// // Create edges between nodes
369    /// builder.add_edge(a, b)?;
370    /// builder.add_edge(b, c)?;
371    ///
372    /// // Create graph from builder
373    /// let graph = builder.build();
374    ///
375    /// // Create iterator over graph
376    /// for node in &graph {
377    ///     println!("{node:?}");
378    /// }
379    /// # Ok(())
380    /// # }
381    /// ```
382    #[inline]
383    fn into_iter(self) -> Self::IntoIter {
384        self.iter()
385    }
386}
387
388// ----------------------------------------------------------------------------
389
390impl<T> Default for Graph<T> {
391    /// Creates an empty graph.
392    ///
393    /// While an empty graph is not very useful, it's sometimes practical as a
394    /// placeholder in documentation, and in types implementing [`Default`].
395    ///
396    /// # Examples
397    ///
398    /// ```
399    /// use zrx_graph::Graph;
400    ///
401    /// // Create empty graph
402    /// let graph = Graph::default();
403    /// # let _: Graph<()> = graph;
404    /// assert!(graph.is_empty());
405    /// ```
406    #[inline]
407    fn default() -> Self {
408        Self::builder().build()
409    }
410}