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}