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}