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}