zrx_graph/graph/builder.rs
1// Copyright (c) Zensical LLC <https://zensical.org>
2
3// SPDX-License-Identifier: MIT
4// Third-party contributions licensed under CLA
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::collections::BTreeMap;
29use std::ops::Index;
30
31use super::error::{Error, Result};
32use super::topology::Topology;
33use super::Graph;
34
35// ----------------------------------------------------------------------------
36// Structs
37// ----------------------------------------------------------------------------
38
39/// Graph builder.
40#[derive(Clone, Debug)]
41pub struct Builder<T, W = ()> {
42 /// Nodes of the graph.
43 nodes: Vec<T>,
44 /// Edges of the graph.
45 edges: Vec<Edge<W>>,
46}
47
48/// Graph edge.
49#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct Edge<W = ()> {
51 /// Source node index.
52 pub source: usize,
53 /// Target node index.
54 pub target: usize,
55 /// Weight.
56 pub weight: W,
57}
58
59// ----------------------------------------------------------------------------
60// Implementations
61// ----------------------------------------------------------------------------
62
63impl<T, W> Builder<T, W> {
64 /// Creates a graph builder.
65 ///
66 /// Note that the canonical way to create a [`Graph`] is to invoke the
67 /// [`Graph::builder`] method, which creates an instance of [`Builder`].
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// # use std::error::Error;
73 /// # fn main() -> Result<(), Box<dyn Error>> {
74 /// use zrx_graph::Builder;
75 ///
76 /// // Create graph builder
77 /// let mut builder = Builder::new();
78 /// # let a = builder.add_node("a");
79 /// # let b = builder.add_node("b");
80 /// #
81 /// # // Create edges between nodes
82 /// # builder.add_edge(a, b, 0)?;
83 /// # Ok(())
84 /// # }
85 /// ```
86 #[must_use]
87 pub fn new() -> Self {
88 Self {
89 nodes: Vec::new(),
90 edges: Vec::new(),
91 }
92 }
93
94 /// Adds a node to the graph.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// # use std::error::Error;
100 /// # fn main() -> Result<(), Box<dyn Error>> {
101 /// use zrx_graph::Graph;
102 ///
103 /// // Create graph builder and add nodes
104 /// let mut builder = Graph::builder();
105 /// let a = builder.add_node("a");
106 /// let b = builder.add_node("b");
107 /// #
108 /// # // Create edges between nodes
109 /// # builder.add_edge(a, b, 0)?;
110 /// # Ok(())
111 /// # }
112 /// ```
113 pub fn add_node(&mut self, node: T) -> usize {
114 self.nodes.push(node);
115 self.nodes.len() - 1
116 }
117
118 /// Adds an edge to the graph.
119 ///
120 /// # Errors
121 ///
122 /// In case the source or target node doesn't exist, [`Error::NotFound`] is
123 /// returned, to make sure the graph does not contain stale node references.
124 /// By returning an error instead of panicking, we can provide recoverable
125 /// and proper error handling to the caller.
126 ///
127 /// This is mentionable, as some other graph libraries will just panic and
128 /// crash the program, like the popular [`petgraph`][] crate. Additionally,
129 /// note that this method does not check whether an edge already exists, as
130 /// the existence of multiple edges is a valid use case in some scenarios.
131 ///
132 /// [`petgraph`]: https://docs.rs/petgraph/
133 ///
134 /// # Examples
135 ///
136 /// ```
137 /// # use std::error::Error;
138 /// # fn main() -> Result<(), Box<dyn Error>> {
139 /// use zrx_graph::Graph;
140 ///
141 /// // Create graph builder and add nodes
142 /// let mut builder = Graph::builder();
143 /// let a = builder.add_node("a");
144 /// let b = builder.add_node("b");
145 /// let c = builder.add_node("c");
146 ///
147 /// // Create edges between nodes
148 /// builder.add_edge(a, b, 0)?;
149 /// builder.add_edge(b, c, 0)?;
150 /// # Ok(())
151 /// # }
152 /// ```
153 pub fn add_edge(
154 &mut self, source: usize, target: usize, weight: W,
155 ) -> Result {
156 if source >= self.nodes.len() {
157 return Err(Error::NotFound(source));
158 }
159 if target >= self.nodes.len() {
160 return Err(Error::NotFound(target));
161 }
162
163 // Add edge, as both nodes were found
164 self.edges.push(Edge { source, target, weight });
165 Ok(())
166 }
167
168 /// Creates the edge graph of the graph.
169 ///
170 /// This method derives a new graph from the given graph in which each edge
171 /// represents a transition from one edge to another based on their source
172 /// and target nodes in the original graph, which means that the nodes of
173 /// the edge graph are the edges of the original graph.
174 ///
175 /// Edge graphs are necessary for representing relationships between edges,
176 /// which is exactly what we need for action graphs, where edges represent
177 /// actions and their dependencies. During execution, we don't need to know
178 /// the actual nodes, but rather the dependencies between the edges.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// # use std::error::Error;
184 /// # fn main() -> Result<(), Box<dyn Error>> {
185 /// use zrx_graph::Graph;
186 ///
187 /// // Create graph builder and add nodes
188 /// let mut builder = Graph::builder();
189 /// let a = builder.add_node("a");
190 /// let b = builder.add_node("b");
191 /// let c = builder.add_node("c");
192 ///
193 /// // Create edges between nodes
194 /// builder.add_edge(a, b, 0)?;
195 /// builder.add_edge(b, c, 0)?;
196 ///
197 /// // Create edge graph
198 /// let edges = builder.to_edge_graph();
199 /// assert_eq!(edges.nodes(), builder.edges());
200 /// # Ok(())
201 /// # }
202 /// ```
203 #[must_use]
204 pub fn to_edge_graph(&self) -> Builder<Edge<W>>
205 where
206 W: Clone,
207 {
208 // We expect that the edges are ordered by target an weight, since the
209 // former represents the corresponding action, and the latter the index
210 // of the argument in the action. This is also why we index sources by
211 // targets and not the other way around, i.e., to keep the ordering.
212 let mut targets: BTreeMap<usize, Vec<usize>> = BTreeMap::new();
213 for (source, edge) in self.edges.iter().enumerate() {
214 targets.entry(edge.target).or_default().push(source);
215 }
216
217 // Enumerate all sources for each target and create the edges between
218 // them in order to create the edge graph. The new edges don't receive
219 // a weight, since the original edges are now the nodes, and there's no
220 // other information that can't be obtained from the original graph.
221 let mut edges = Vec::with_capacity(targets.len());
222 for (target, edge) in self.edges.iter().enumerate() {
223 if let Some(sources) = targets.get(&edge.source) {
224 for &source in sources {
225 edges.push(Edge { source, target, weight: () });
226 }
227 }
228 }
229
230 // Return edge graph builder
231 Builder {
232 nodes: self.edges.clone(),
233 edges,
234 }
235 }
236
237 /// Builds the graph.
238 ///
239 /// This method creates the actual graph from the builder, which brings the
240 /// graph into an executable form to allow for very efficient traversal.
241 ///
242 /// # Examples
243 ///
244 /// ```
245 /// # use std::error::Error;
246 /// # fn main() -> Result<(), Box<dyn Error>> {
247 /// use zrx_graph::Graph;
248 ///
249 /// // Create graph builder and add nodes
250 /// let mut builder = Graph::builder();
251 /// let a = builder.add_node("a");
252 /// let b = builder.add_node("b");
253 /// let c = builder.add_node("c");
254 ///
255 /// // Create edges between nodes
256 /// builder.add_edge(a, b, 0)?;
257 /// builder.add_edge(b, c, 0)?;
258 ///
259 /// // Create graph from builder
260 /// let graph = builder.build();
261 /// # Ok(())
262 /// # }
263 /// ```
264 #[must_use]
265 pub fn build(self) -> Graph<T>
266 where
267 W: Clone,
268 {
269 Graph {
270 topology: Topology::new(&self),
271 data: self.nodes,
272 }
273 }
274}
275
276#[allow(clippy::must_use_candidate)]
277impl<T, W> Builder<T, W> {
278 /// Returns a reference to the nodes.
279 #[inline]
280 pub fn nodes(&self) -> &[T] {
281 &self.nodes
282 }
283
284 /// Returns a reference to the edges.
285 #[inline]
286 pub fn edges(&self) -> &[Edge<W>] {
287 &self.edges
288 }
289
290 /// Returns the number of nodes.
291 #[inline]
292 pub fn len(&self) -> usize {
293 self.nodes.len()
294 }
295
296 /// Returns whether there are any nodes.
297 #[inline]
298 pub fn is_empty(&self) -> bool {
299 self.nodes.is_empty()
300 }
301}
302
303// ----------------------------------------------------------------------------
304// Trait implementations
305// ----------------------------------------------------------------------------
306
307impl<T, W> Index<usize> for Builder<T, W> {
308 type Output = T;
309
310 /// Returns a reference to the node at the index.
311 ///
312 /// # Panics
313 ///
314 /// Panics if the index is out of bounds.
315 ///
316 /// # Examples
317 ///
318 /// ```
319 /// # use std::error::Error;
320 /// # fn main() -> Result<(), Box<dyn Error>> {
321 /// use zrx_graph::topology::Adjacency;
322 /// use zrx_graph::Graph;
323 ///
324 /// // Create graph builder and add nodes
325 /// let mut builder = Graph::builder();
326 /// let a = builder.add_node("a");
327 /// let b = builder.add_node("b");
328 /// let c = builder.add_node("c");
329 ///
330 /// // Create edges between nodes
331 /// builder.add_edge(a, b, 0)?;
332 /// builder.add_edge(b, c, 0)?;
333 ///
334 /// // Obtain references to nodes
335 /// assert_eq!(&builder[a], &"a");
336 /// assert_eq!(&builder[b], &"b");
337 /// assert_eq!(&builder[c], &"c");
338 /// # Ok(())
339 /// # }
340 /// ```
341 #[inline]
342 fn index(&self, index: usize) -> &Self::Output {
343 &self.nodes[index]
344 }
345}
346
347// ----------------------------------------------------------------------------
348
349impl<T, W> Default for Builder<T, W>
350where
351 W: Clone,
352{
353 /// Creates a graph builder.
354 ///
355 /// # Examples
356 ///
357 /// ```
358 /// # use std::error::Error;
359 /// # fn main() -> Result<(), Box<dyn Error>> {
360 /// use zrx_graph::Builder;
361 ///
362 /// // Create graph builder
363 /// let mut builder = Builder::default();
364 /// # let a = builder.add_node("a");
365 /// # let b = builder.add_node("b");
366 /// #
367 /// # // Create edges between nodes
368 /// # builder.add_edge(a, b, 0)?;
369 /// # Ok(())
370 /// # }
371 /// ```
372 #[inline]
373 fn default() -> Self {
374 Self::new()
375 }
376}