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}