zrx_graph/graph/visitor/
descendants.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//! Visitor for descendants of a node.
27
28use ahash::HashSet;
29
30use crate::graph::topology::Topology;
31use crate::graph::Graph;
32
33// ----------------------------------------------------------------------------
34// Structs
35// ----------------------------------------------------------------------------
36
37/// Visitor for descendants of a node.
38pub struct Descendants<'a> {
39    /// Graph topology.
40    topology: &'a Topology,
41    /// Stack for depth-first search.
42    stack: Vec<usize>,
43    /// Set of visited nodes.
44    visited: HashSet<usize>,
45}
46
47// ----------------------------------------------------------------------------
48// Implementations
49// ----------------------------------------------------------------------------
50
51impl<T> Graph<T> {
52    /// Creates an iterator over the descendants of the given node.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// # use std::error::Error;
58    /// # fn main() -> Result<(), Box<dyn Error>> {
59    /// use zrx_graph::Graph;
60    ///
61    /// // Create graph builder and add nodes
62    /// let mut builder = Graph::builder();
63    /// let a = builder.add_node("a");
64    /// let b = builder.add_node("b");
65    /// let c = builder.add_node("c");
66    ///
67    /// // Create edges between nodes
68    /// builder.add_edge(a, b, 0)?;
69    /// builder.add_edge(b, c, 0)?;
70    ///
71    /// // Create graph from builder
72    /// let graph = builder.build();
73    ///
74    /// // Create iterator over descendants
75    /// for node in graph.descendants(a) {
76    ///     println!("{node:?}");
77    /// }
78    /// # Ok(())
79    /// # }
80    /// ```
81    #[inline]
82    #[must_use]
83    pub fn descendants(&self, node: usize) -> Descendants<'_> {
84        Descendants {
85            topology: &self.topology,
86            stack: vec![node],
87            visited: HashSet::default(),
88        }
89    }
90}
91
92// ----------------------------------------------------------------------------
93// Trait implementations
94// ----------------------------------------------------------------------------
95
96impl Iterator for Descendants<'_> {
97    type Item = usize;
98
99    /// Returns the next descendant.
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 and add nodes
109    /// let mut builder = Graph::builder();
110    /// let a = builder.add_node("a");
111    /// let b = builder.add_node("b");
112    /// let c = builder.add_node("c");
113    ///
114    /// // Create edges between nodes
115    /// builder.add_edge(a, b, 0)?;
116    /// builder.add_edge(b, c, 0)?;
117    ///
118    /// // Create graph from builder
119    /// let graph = builder.build();
120    ///
121    /// // Create iterator over descendants
122    /// let mut descendants = graph.descendants(a);
123    /// while let Some(node) = descendants.next() {
124    ///     println!("{node:?}");
125    /// }
126    /// # Ok(())
127    /// # }
128    /// ```
129    fn next(&mut self) -> Option<Self::Item> {
130        let outgoing = self.topology.outgoing();
131
132        // Perform a depth-first search to find all descendants, using a stack
133        // over recursion, as it's faster and more efficient memory-wise
134        while let Some(node) = self.stack.pop() {
135            for &descendant in &outgoing[node] {
136                // If we haven't visited this descendant yet, we put it on the
137                // stack after marking it as visited and return it immediately
138                if self.visited.insert(descendant) {
139                    self.stack.push(descendant);
140                    return Some(descendant);
141                }
142            }
143        }
144
145        // No more descendants to visit
146        None
147    }
148}