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}