zrx_graph/graph.rs
1// Copyright (c) 2025 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// Third-party contributions licensed under 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};
29use std::slice::Iter;
30
31pub mod algorithm;
32mod builder;
33mod error;
34pub mod topology;
35pub mod traversal;
36pub mod visitor;
37
38pub use builder::Builder;
39pub use error::{Error, Result};
40use topology::Topology;
41use traversal::Traversal;
42
43// ----------------------------------------------------------------------------
44// Structs
45// ----------------------------------------------------------------------------
46
47/// Graph.
48///
49/// This data type represents a directed graph with nodes of type `T`, which is
50/// optimized for very efficient traversal, since it offers lookups of nodes and
51/// edges in O(1), i.e., constant time. It's built with the [`Graph::builder`]
52/// method, which allows to add nodes and edges, before building the graph.
53///
54/// Note that this graph implementation is unweighted, which means edges do not
55/// carry associated weights, something that we don't need for our case.
56///
57/// # Examples
58///
59/// ```
60/// # use std::error::Error;
61/// # fn main() -> Result<(), Box<dyn Error>> {
62/// use zrx_graph::Graph;
63///
64/// // Create graph builder and add nodes
65/// let mut builder = Graph::builder();
66/// let a = builder.add_node("a");
67/// let b = builder.add_node("b");
68/// let c = builder.add_node("c");
69///
70/// // Create edges between nodes
71/// builder.add_edge(a, b, 0)?;
72/// builder.add_edge(b, c, 0)?;
73///
74/// // Create graph from builder
75/// let graph = builder.build();
76///
77/// // Create topological traversal
78/// let mut traversal = graph.traverse([a]);
79/// while let Some(node) = traversal.take() {
80/// println!("{node:?}");
81/// traversal.complete(node)?;
82/// }
83/// # Ok(())
84/// # }
85/// ```
86#[derive(Clone, Debug)]
87pub struct Graph<T> {
88 /// Graph data.
89 data: Vec<T>,
90 /// Graph topology.
91 topology: Topology,
92}
93
94// ----------------------------------------------------------------------------
95// Implementations
96// ----------------------------------------------------------------------------
97
98impl<T> Graph<T> {
99 /// Creates a graph builder.
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// # use std::error::Error;
105 /// # fn main() -> Result<(), Box<dyn Error>> {
106 /// use zrx_graph::Graph;
107 ///
108 /// // Create graph builder
109 /// let mut builder = Graph::builder();
110 /// let a = builder.add_node("a");
111 /// let b = builder.add_node("b");
112 ///
113 /// // Create edges between nodes
114 /// builder.add_edge(a, b, 0)?;
115 /// # Ok(())
116 /// # }
117 /// ```
118 #[inline]
119 #[must_use]
120 pub fn builder<W>() -> Builder<T, W>
121 where
122 W: Clone,
123 {
124 Builder::new()
125 }
126
127 /// Creates an empty graph.
128 ///
129 /// While an empty graph is not very useful, it's sometimes practical as a
130 /// placeholder in documentation or examples, where a graph is expected.
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use zrx_graph::Graph;
136 ///
137 /// // Create empty graph
138 /// let graph = Graph::empty();
139 /// # let _: Graph<()> = graph;
140 /// assert!(graph.is_empty());
141 /// ```
142 #[inline]
143 #[must_use]
144 pub fn empty() -> Self {
145 Builder::<T>::new().build()
146 }
147
148 /// Maps the nodes to a different type.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// # use std::error::Error;
154 /// # fn main() -> Result<(), Box<dyn Error>> {
155 /// use zrx_graph::Graph;
156 ///
157 /// // Create graph builder and add nodes
158 /// let mut builder = Graph::builder();
159 /// let a = builder.add_node("a");
160 /// let b = builder.add_node("b");
161 /// let c = builder.add_node("c");
162 ///
163 /// // Create edges between nodes
164 /// builder.add_edge(a, b, 0)?;
165 /// builder.add_edge(b, c, 0)?;
166 ///
167 /// // Create graph from builder and map data
168 /// let graph = builder.build();
169 /// graph.map(str::to_uppercase);
170 /// # Ok(())
171 /// # }
172 /// ```
173 #[inline]
174 pub fn map<F, U>(self, f: F) -> Graph<U>
175 where
176 F: FnMut(T) -> U,
177 {
178 Graph {
179 data: self.data.into_iter().map(f).collect(),
180 topology: self.topology,
181 }
182 }
183
184 /// Creates a topogical traversal starting from the given initial nodes.
185 ///
186 /// This method creates a topological traversal of the graph, which allows
187 /// to visit nodes in a topological order, i.e., visiting a node only after
188 /// all its dependencies have been visited. The traversal is initialized
189 /// with the given initial nodes, which are the starting points.
190 ///
191 /// Note that an arbitrary number of parallel traversals can be created
192 /// from the same graph, as the underlying topology is shared between them.
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// # use std::error::Error;
198 /// # fn main() -> Result<(), Box<dyn Error>> {
199 /// use zrx_graph::Graph;
200 ///
201 /// // Create graph builder and add nodes
202 /// let mut builder = Graph::builder();
203 /// let a = builder.add_node("a");
204 /// let b = builder.add_node("b");
205 /// let c = builder.add_node("c");
206 ///
207 /// // Create edges between nodes
208 /// builder.add_edge(a, b, 0)?;
209 /// builder.add_edge(b, c, 0)?;
210 ///
211 /// // Create graph from builder
212 /// let graph = builder.build();
213 ///
214 /// // Create topological traversal
215 /// let mut traversal = graph.traverse([a]);
216 /// while let Some(node) = traversal.take() {
217 /// println!("{node:?}");
218 /// traversal.complete(node)?;
219 /// }
220 /// # Ok(())
221 /// # }
222 /// ```
223 #[inline]
224 pub fn traverse<I>(&self, initial: I) -> Traversal
225 where
226 I: IntoIterator<Item = usize>,
227 {
228 Traversal::new(&self.topology, initial)
229 }
230
231 /// Creates an iterator over the sources of the graph.
232 ///
233 /// This method returns an iterator over the source node indices of the
234 /// graph, which are the nodes with no incoming edges.
235 ///
236 /// # Examples
237 ///
238 /// ```
239 /// # use std::error::Error;
240 /// # fn main() -> Result<(), Box<dyn Error>> {
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, 0)?;
251 /// builder.add_edge(b, c, 0)?;
252 ///
253 /// // Create graph from builder
254 /// let graph = builder.build();
255 ///
256 /// // Create iterator over sources
257 /// for node in graph.sources() {
258 /// println!("{node:?}");
259 /// }
260 /// # Ok(())
261 /// # }
262 /// ```
263 #[inline]
264 pub fn sources(&self) -> impl Iterator<Item = usize> {
265 let incoming = self.topology.incoming();
266 incoming.iter().filter(|&node| incoming[node].is_empty())
267 }
268
269 /// Creates an iterator over the sinks of the graph.
270 ///
271 /// This method returns an iterator over the sink node indices of the
272 /// graph, which are the nodes with no incoming edges.
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// # use std::error::Error;
278 /// # fn main() -> Result<(), Box<dyn Error>> {
279 /// use zrx_graph::Graph;
280 ///
281 /// // Create graph builder and add nodes
282 /// let mut builder = Graph::builder();
283 /// let a = builder.add_node("a");
284 /// let b = builder.add_node("b");
285 /// let c = builder.add_node("c");
286 ///
287 /// // Create edges between nodes
288 /// builder.add_edge(a, b, 0)?;
289 /// builder.add_edge(b, c, 0)?;
290 ///
291 /// // Create graph from builder
292 /// let graph = builder.build();
293 ///
294 /// // Create iterator over sinks
295 /// for node in graph.sinks() {
296 /// println!("{node:?}");
297 /// }
298 /// # Ok(())
299 /// # }
300 /// ```
301 #[inline]
302 pub fn sinks(&self) -> impl Iterator<Item = usize> {
303 let outgoing = self.topology.outgoing();
304 outgoing.iter().filter(|&node| outgoing[node].is_empty())
305 }
306
307 /// Creates an iterator over the graph.
308 ///
309 /// This iterator emits the data `T` associated with each node. If you need
310 /// to iterate over the node indices of a graph, use [`Graph::topology`] to
311 /// obtain the [`Topology::incoming`] or [`Topology::outgoing`] adjacency
312 /// list, and iterate over those.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// # use std::error::Error;
318 /// # fn main() -> Result<(), Box<dyn Error>> {
319 /// use zrx_graph::topology::Adjacency;
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, 0)?;
330 /// builder.add_edge(b, c, 0)?;
331 ///
332 /// // Create graph from builder
333 /// let graph = builder.build();
334 ///
335 /// // Create iterator over data
336 /// for data in graph.iter() {
337 /// println!("{data:?}");
338 /// }
339 /// # Ok(())
340 /// # }
341 /// ```
342 #[inline]
343 pub fn iter(&self) -> Iter<'_, T> {
344 self.data.iter()
345 }
346}
347
348#[allow(clippy::must_use_candidate)]
349impl<T> Graph<T> {
350 /// Returns the graph topology.
351 #[inline]
352 pub fn topology(&self) -> &Topology {
353 &self.topology
354 }
355
356 /// Returns the number of nodes.
357 #[inline]
358 pub fn len(&self) -> usize {
359 self.data.len()
360 }
361
362 /// Returns whether there are any nodes.
363 #[inline]
364 pub fn is_empty(&self) -> bool {
365 self.data.is_empty()
366 }
367
368 /// Returns whether the given node is a source.
369 #[inline]
370 pub fn is_source(&self, node: usize) -> bool {
371 let incoming = self.topology.incoming();
372 incoming[node].is_empty()
373 }
374
375 /// Returns whether the given node is a sink.
376 #[inline]
377 pub fn is_sink(&self, node: usize) -> bool {
378 let outgoing = self.topology.outgoing();
379 outgoing[node].is_empty()
380 }
381}
382
383// ----------------------------------------------------------------------------
384// Trait implementations
385// ----------------------------------------------------------------------------
386
387impl<T> Index<usize> for Graph<T> {
388 type Output = T;
389
390 /// Returns a reference to the node at the index.
391 ///
392 /// # Panics
393 ///
394 /// Panics if the index is out of bounds.
395 ///
396 /// # Examples
397 ///
398 /// ```
399 /// # use std::error::Error;
400 /// # fn main() -> Result<(), Box<dyn Error>> {
401 /// use zrx_graph::topology::Adjacency;
402 /// use zrx_graph::Graph;
403 ///
404 /// // Create graph builder and add nodes
405 /// let mut builder = Graph::builder();
406 /// let a = builder.add_node("a");
407 /// let b = builder.add_node("b");
408 /// let c = builder.add_node("c");
409 ///
410 /// // Create edges between nodes
411 /// builder.add_edge(a, b, 0)?;
412 /// builder.add_edge(b, c, 0)?;
413 ///
414 /// // Create graph from builder
415 /// let graph = builder.build();
416 ///
417 /// // Obtain references to nodes
418 /// assert_eq!(&graph[a], &"a");
419 /// assert_eq!(&graph[b], &"b");
420 /// assert_eq!(&graph[c], &"c");
421 /// # Ok(())
422 /// # }
423 /// ```
424 #[inline]
425 fn index(&self, index: usize) -> &Self::Output {
426 &self.data[index]
427 }
428}
429
430impl<T> IndexMut<usize> for Graph<T> {
431 /// Returns a mutable reference to the node at the index.
432 ///
433 /// # Panics
434 ///
435 /// Panics if the index is out of bounds.
436 ///
437 /// # Examples
438 ///
439 /// ```
440 /// # use std::error::Error;
441 /// # fn main() -> Result<(), Box<dyn Error>> {
442 /// use zrx_graph::topology::Adjacency;
443 /// use zrx_graph::Graph;
444 ///
445 /// // Create graph builder and add nodes
446 /// let mut builder = Graph::builder();
447 /// let a = builder.add_node("a");
448 /// let b = builder.add_node("b");
449 /// let c = builder.add_node("c");
450 ///
451 /// // Create edges between nodes
452 /// builder.add_edge(a, b, 0)?;
453 /// builder.add_edge(b, c, 0)?;
454 ///
455 /// // Create graph from builder
456 /// let mut graph = builder.build();
457 ///
458 /// // Obtain mutable references to nodes
459 /// assert_eq!(&mut graph[a], &mut "a");
460 /// assert_eq!(&mut graph[b], &mut "b");
461 /// assert_eq!(&mut graph[c], &mut "c");
462 /// # Ok(())
463 /// # }
464 /// ```
465 #[inline]
466 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
467 &mut self.data[index]
468 }
469}
470
471// ----------------------------------------------------------------------------
472
473impl<'a, T> IntoIterator for &'a Graph<T> {
474 type Item = &'a T;
475 type IntoIter = Iter<'a, T>;
476
477 /// Creates an iterator over the graph.
478 ///
479 /// This iterator emits the data `T` associated with each node. If you need
480 /// to iterate over the node indices of a graph, use [`Graph::topology`] to
481 /// obtain the [`Topology::incoming`] or [`Topology::outgoing`] adjacency
482 /// list, and iterate over those.
483 ///
484 /// # Examples
485 ///
486 /// ```
487 /// # use std::error::Error;
488 /// # fn main() -> Result<(), Box<dyn Error>> {
489 /// use zrx_graph::topology::Adjacency;
490 /// use zrx_graph::Graph;
491 ///
492 /// // Create graph builder and add nodes
493 /// let mut builder = Graph::builder();
494 /// let a = builder.add_node("a");
495 /// let b = builder.add_node("b");
496 /// let c = builder.add_node("c");
497 ///
498 /// // Create edges between nodes
499 /// builder.add_edge(a, b, 0)?;
500 /// builder.add_edge(b, c, 0)?;
501 ///
502 /// // Create graph from builder
503 /// let graph = builder.build();
504 ///
505 /// // Create iterator over data
506 /// for data in &graph {
507 /// println!("{data:?}");
508 /// }
509 /// # Ok(())
510 /// # }
511 /// ```
512 #[inline]
513 fn into_iter(self) -> Self::IntoIter {
514 self.iter()
515 }
516}