Skip to main content

zrx_graph/graph/iter/
descendants.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//! Iterator over descendants of a node.
27
28use ahash::HashSet;
29
30use crate::graph::topology::Adjacency;
31use crate::graph::Graph;
32
33// ----------------------------------------------------------------------------
34// Structs
35// ----------------------------------------------------------------------------
36
37/// Iterator over descendants of a node.
38pub struct Descendants<'a> {
39    /// Outgoing edges.
40    outgoing: &'a Adjacency,
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    /// # Panics
55    ///
56    /// Panics if the node does not exist, as this indicates that there's a bug
57    /// in the code that creates or uses the iterator. While the [`Builder`][]
58    /// is designed to be fallible to ensure the structure is valid, methods
59    /// that operate on [`Graph`] panic on violated invariants.
60    ///
61    /// [`Builder`]: crate::graph::Builder
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// # use std::error::Error;
67    /// # fn main() -> Result<(), Box<dyn Error>> {
68    /// use zrx_graph::Graph;
69    ///
70    /// // Create graph builder and add nodes
71    /// let mut builder = Graph::builder();
72    /// let a = builder.add_node("a");
73    /// let b = builder.add_node("b");
74    /// let c = builder.add_node("c");
75    ///
76    /// // Create edges between nodes
77    /// builder.add_edge(a, b)?;
78    /// builder.add_edge(b, c)?;
79    ///
80    /// // Create graph from builder
81    /// let graph = builder.build();
82    ///
83    /// // Create iterator over descendants
84    /// for node in graph.descendants(a) {
85    ///     println!("{node:?}");
86    /// }
87    /// # Ok(())
88    /// # }
89    /// ```
90    #[inline]
91    #[must_use]
92    pub fn descendants(&self, node: usize) -> Descendants<'_> {
93        let mut iter = Descendants {
94            outgoing: self.topology.outgoing(),
95            stack: Vec::from([node]),
96            visited: HashSet::default(),
97        };
98
99        // Skip the initial node itself - it's simpler to just skip the initial
100        // node, so we can keep the iterator implementation plain and simple
101        iter.next();
102        iter
103    }
104}
105
106// ----------------------------------------------------------------------------
107// Trait implementations
108// ----------------------------------------------------------------------------
109
110impl Iterator for Descendants<'_> {
111    type Item = usize;
112
113    /// Returns the next descendant.
114    fn next(&mut self) -> Option<Self::Item> {
115        // Perform a depth-first search to find all descendants of a node, by
116        // exploring them iteratively, not including the node itself
117        let node = self.stack.pop()?;
118        for &descendant in self.outgoing[node].iter().rev() {
119            // If we haven't visited this descendant yet, we put it on the
120            // stack after marking it as visited and return it immediately
121            if self.visited.insert(descendant) {
122                self.stack.push(descendant);
123            }
124        }
125
126        // Return the next descendant
127        Some(node)
128    }
129}
130
131// ----------------------------------------------------------------------------
132// Tests
133// ----------------------------------------------------------------------------
134
135#[cfg(test)]
136mod tests {
137
138    mod descendants {
139        use crate::graph;
140
141        #[test]
142        fn handles_graph() {
143            let graph = graph! {
144                "a" => "b", "a" => "c",
145                "b" => "d", "b" => "e",
146                "c" => "f",
147                "d" => "g",
148                "e" => "g", "e" => "h",
149                "f" => "h",
150                "g" => "i",
151                "h" => "i",
152            };
153            for (node, descendants) in [
154                (0, vec![1, 3, 6, 8, 4, 7, 2, 5]),
155                (1, vec![3, 6, 8, 4, 7]),
156                (2, vec![5, 7, 8]),
157                (3, vec![6, 8]),
158                (4, vec![6, 8, 7]),
159                (5, vec![7, 8]),
160                (6, vec![8]),
161                (7, vec![8]),
162                (8, vec![]),
163            ] {
164                assert_eq!(
165                    graph.descendants(node).collect::<Vec<_>>(),
166                    descendants
167                );
168            }
169        }
170
171        #[test]
172        fn handles_multi_graph() {
173            let graph = graph! {
174                "a" => "b", "a" => "c", "a" => "c",
175                "b" => "d", "b" => "e",
176                "c" => "f",
177                "d" => "g",
178                "e" => "g", "e" => "h",
179                "f" => "h",
180                "g" => "i",
181                "h" => "i",
182            };
183            for (node, descendants) in [
184                (0, vec![1, 3, 6, 8, 4, 7, 2, 5]),
185                (1, vec![3, 6, 8, 4, 7]),
186                (2, vec![5, 7, 8]),
187                (3, vec![6, 8]),
188                (4, vec![6, 8, 7]),
189                (5, vec![7, 8]),
190                (6, vec![8]),
191                (7, vec![8]),
192                (8, vec![]),
193            ] {
194                assert_eq!(
195                    graph.descendants(node).collect::<Vec<_>>(),
196                    descendants
197                );
198            }
199        }
200    }
201}