Skip to main content

zrx_graph/graph/
builder.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 builder.
27
28use std::ops::{Index, Range};
29
30use super::error::{Error, Result};
31use super::topology::Topology;
32use super::Graph;
33
34// ----------------------------------------------------------------------------
35// Structs
36// ----------------------------------------------------------------------------
37
38/// Graph builder.
39#[derive(Clone, Debug)]
40pub struct Builder<T> {
41    /// Nodes of the graph.
42    nodes: Vec<T>,
43    /// Edges of the graph.
44    edges: Vec<Edge>,
45}
46
47/// Graph edge.
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct Edge {
50    /// Source node index.
51    pub source: usize,
52    /// Target node index.
53    pub target: usize,
54}
55
56// ----------------------------------------------------------------------------
57// Implementations
58// ----------------------------------------------------------------------------
59
60impl<T> Graph<T> {
61    /// Creates a 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
71    /// let mut builder = Graph::builder();
72    /// let a = builder.add_node("a");
73    /// let b = builder.add_node("b");
74    ///
75    /// // Create edges between nodes
76    /// builder.add_edge(a, b)?;
77    /// # Ok(())
78    /// # }
79    /// ```
80    #[inline]
81    #[must_use]
82    pub fn builder() -> Builder<T> {
83        Builder::default()
84    }
85}
86
87// ----------------------------------------------------------------------------
88
89impl<T> Builder<T> {
90    /// Adds a node to the graph.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// # use std::error::Error;
96    /// # fn main() -> Result<(), Box<dyn Error>> {
97    /// use zrx_graph::Graph;
98    ///
99    /// // Create graph builder and add nodes
100    /// let mut builder = Graph::builder();
101    /// let a = builder.add_node("a");
102    /// let b = builder.add_node("b");
103    /// #
104    /// # // Create edges between nodes
105    /// # builder.add_edge(a, b)?;
106    /// # Ok(())
107    /// # }
108    /// ```
109    pub fn add_node(&mut self, data: T) -> usize {
110        self.nodes.push(data);
111        self.nodes.len() - 1
112    }
113
114    /// Adds an edge to the graph.
115    ///
116    /// # Errors
117    ///
118    /// In case the source or target node doesn't exist, [`Error::NotFound`] is
119    /// returned, to make sure the graph does not contain stale node references.
120    /// By returning an error instead of panicking, we can provide recoverable
121    /// and proper error handling to the caller.
122    ///
123    /// This is mentionable, as some other graph libraries will just panic and
124    /// crash the program, like the popular [`petgraph`][] crate. Additionally,
125    /// note that this method does not check whether an edge already exists, as
126    /// the existence of multiple edges is a valid use case in some scenarios.
127    ///
128    /// [`petgraph`]: https://docs.rs/petgraph/
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// # use std::error::Error;
134    /// # fn main() -> Result<(), Box<dyn Error>> {
135    /// use zrx_graph::Graph;
136    ///
137    /// // Create graph builder and add nodes
138    /// let mut builder = Graph::builder();
139    /// let a = builder.add_node("a");
140    /// let b = builder.add_node("b");
141    /// let c = builder.add_node("c");
142    ///
143    /// // Create edges between nodes
144    /// builder.add_edge(a, b)?;
145    /// builder.add_edge(b, c)?;
146    /// # Ok(())
147    /// # }
148    /// ```
149    pub fn add_edge(&mut self, source: usize, target: usize) -> Result {
150        if source >= self.nodes.len() {
151            return Err(Error::NotFound(source));
152        }
153        if target >= self.nodes.len() {
154            return Err(Error::NotFound(target));
155        }
156
157        // Add edge, as both nodes were found
158        self.edges.push(Edge { source, target });
159        Ok(())
160    }
161
162    /// Creates an iterator over the graph builder.
163    ///
164    /// This iterator emits the node indices, which is exactly the same as
165    /// iterating over the adjacency list using `0..self.len()`.
166    ///
167    /// # Examples
168    ///
169    /// ```
170    /// # use std::error::Error;
171    /// # fn main() -> Result<(), Box<dyn Error>> {
172    /// use zrx_graph::topology::Adjacency;
173    /// use zrx_graph::Graph;
174    ///
175    /// // Create graph builder and add nodes
176    /// let mut builder = Graph::builder();
177    /// let a = builder.add_node("a");
178    /// let b = builder.add_node("b");
179    /// let c = builder.add_node("c");
180    ///
181    /// // Create edges between nodes
182    /// builder.add_edge(a, b)?;
183    /// builder.add_edge(b, c)?;
184    ///
185    /// // Create iterator over graph builder
186    /// for node in builder.iter() {
187    ///     println!("{node:?}");
188    /// }
189    /// # Ok(())
190    /// # }
191    /// ```
192    #[inline]
193    #[must_use]
194    pub fn iter(&self) -> Range<usize> {
195        0..self.nodes.len()
196    }
197
198    /// Builds the graph.
199    ///
200    /// This method creates the actual graph from the builder, which brings the
201    /// graph into an executable form to allow for very efficient traversal.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// # use std::error::Error;
207    /// # fn main() -> Result<(), Box<dyn Error>> {
208    /// use zrx_graph::Graph;
209    ///
210    /// // Create graph builder and add nodes
211    /// let mut builder = Graph::builder();
212    /// let a = builder.add_node("a");
213    /// let b = builder.add_node("b");
214    /// let c = builder.add_node("c");
215    ///
216    /// // Create edges between nodes
217    /// builder.add_edge(a, b)?;
218    /// builder.add_edge(b, c)?;
219    ///
220    /// // Create graph from builder
221    /// let graph = builder.build();
222    /// # Ok(())
223    /// # }
224    /// ```
225    #[must_use]
226    pub fn build(self) -> Graph<T> {
227        let topology = Topology::new(self.nodes.len(), &self.edges);
228        Graph { data: self.nodes, topology }
229    }
230}
231
232#[allow(clippy::must_use_candidate)]
233impl<T> Builder<T> {
234    /// Returns a reference to the nodes.
235    #[inline]
236    pub fn nodes(&self) -> &[T] {
237        &self.nodes
238    }
239
240    /// Returns a reference to the edges.
241    #[inline]
242    pub fn edges(&self) -> &[Edge] {
243        &self.edges
244    }
245
246    /// Returns the number of nodes.
247    #[inline]
248    pub fn len(&self) -> usize {
249        self.nodes.len()
250    }
251
252    /// Returns whether there are any nodes.
253    #[inline]
254    pub fn is_empty(&self) -> bool {
255        self.nodes.is_empty()
256    }
257}
258
259// ----------------------------------------------------------------------------
260// Trait implementations
261// ----------------------------------------------------------------------------
262
263impl<T> Index<usize> for Builder<T> {
264    type Output = T;
265
266    /// Returns a reference to the node at the index.
267    ///
268    /// # Panics
269    ///
270    /// Panics if the index is out of bounds.
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// # use std::error::Error;
276    /// # fn main() -> Result<(), Box<dyn Error>> {
277    /// use zrx_graph::topology::Adjacency;
278    /// use zrx_graph::Graph;
279    ///
280    /// // Create graph builder and add nodes
281    /// let mut builder = Graph::builder();
282    /// let a = builder.add_node("a");
283    /// let b = builder.add_node("b");
284    /// let c = builder.add_node("c");
285    ///
286    /// // Create edges between nodes
287    /// builder.add_edge(a, b)?;
288    /// builder.add_edge(b, c)?;
289    ///
290    /// // Obtain references to nodes
291    /// assert_eq!(&builder[a], &"a");
292    /// assert_eq!(&builder[b], &"b");
293    /// assert_eq!(&builder[c], &"c");
294    /// # Ok(())
295    /// # }
296    /// ```
297    #[inline]
298    fn index(&self, index: usize) -> &Self::Output {
299        &self.nodes[index]
300    }
301}
302
303// ----------------------------------------------------------------------------
304
305impl<T> IntoIterator for &Builder<T> {
306    type Item = usize;
307    type IntoIter = Range<usize>;
308
309    /// Creates an iterator over the graph builder.
310    ///
311    /// This iterator emits the node indices, which is exactly the same as
312    /// iterating over the adjacency list using `0..self.len()`.
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)?;
330    /// builder.add_edge(b, c)?;
331    ///
332    /// // Create iterator over graph builder
333    /// for node in &builder {
334    ///     println!("{node:?}");
335    /// }
336    /// # Ok(())
337    /// # }
338    /// ```
339    #[inline]
340    fn into_iter(self) -> Self::IntoIter {
341        self.iter()
342    }
343}
344
345// ----------------------------------------------------------------------------
346
347impl<T> Default for Builder<T> {
348    /// Creates a graph builder.
349    ///
350    /// # Examples
351    ///
352    /// ```
353    /// use zrx_graph::Builder;
354    ///
355    /// // Create graph builder
356    /// let mut builder = Builder::default();
357    /// # let _: Builder<()> = builder;
358    /// ```
359    #[inline]
360    fn default() -> Self {
361        Self {
362            nodes: Vec::default(),
363            edges: Vec::default(),
364        }
365    }
366}