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::{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}