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}