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