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