graph_core/
builder.rs

1use crate::{AdjacencyList, AdjacencyMatrix, Graph, NodeId};
2
3/// A fluent builder for constructing graphs from either representation.
4///
5/// Call [`GraphBuilder::new`], chain configuration and node/edge additions,
6/// then finalise with [`build_adjacency_list`](GraphBuilder::build_adjacency_list)
7/// or [`build_adjacency_matrix`](GraphBuilder::build_adjacency_matrix).
8///
9/// # Examples
10///
11/// ```
12/// use graph_core::{GraphBuilder, Graph};
13///
14/// let g = GraphBuilder::<&str, f64>::new()
15///     .directed()
16///     .node("A")
17///     .node("B")
18///     .node("C")
19///     .edge(0, 1, 1.5)
20///     .edge(1, 2, 2.0)
21///     .build_adjacency_list();
22///
23/// assert_eq!(g.node_count(), 3);
24/// assert_eq!(g.edge_count(), 2);
25/// ```
26#[derive(Debug)]
27pub struct GraphBuilder<N, W = f64> {
28    nodes: Vec<N>,
29    edges: Vec<(usize, usize, W)>,
30    directed: bool,
31}
32
33// ── Construction ──────────────────────────────────────────────────────────────
34
35impl<N, W> GraphBuilder<N, W> {
36    /// Creates a new builder with no nodes or edges (directed by default).
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use graph_core::GraphBuilder;
42    ///
43    /// let b: GraphBuilder<(), f64> = GraphBuilder::new();
44    /// ```
45    pub fn new() -> Self {
46        Self {
47            nodes: Vec::new(),
48            edges: Vec::new(),
49            directed: true,
50        }
51    }
52}
53
54impl<N, W> Default for GraphBuilder<N, W> {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60// ── Configuration ─────────────────────────────────────────────────────────────
61
62impl<N, W> GraphBuilder<N, W> {
63    /// Configures the graph to be **directed** (default).
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use graph_core::GraphBuilder;
69    ///
70    /// let b: GraphBuilder<(), f64> = GraphBuilder::new().directed();
71    /// ```
72    #[inline]
73    pub fn directed(mut self) -> Self {
74        self.directed = true;
75        self
76    }
77
78    /// Configures the graph to be **undirected**.
79    ///
80    /// Each call to [`edge`](Self::edge) will insert edges in both directions.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use graph_core::{GraphBuilder, Graph};
86    ///
87    /// let g = GraphBuilder::<(), f64>::new()
88    ///     .undirected()
89    ///     .node(())
90    ///     .node(())
91    ///     .edge(0, 1, 1.0)
92    ///     .build_adjacency_list();
93    ///
94    /// assert!(g.contains_edge(0usize.into(), 1usize.into()));
95    /// assert!(g.contains_edge(1usize.into(), 0usize.into()));
96    /// ```
97    #[inline]
98    pub fn undirected(mut self) -> Self {
99        self.directed = false;
100        self
101    }
102}
103
104// ── Node / edge accumulation ──────────────────────────────────────────────────
105
106impl<N, W> GraphBuilder<N, W> {
107    /// Adds a node with the given data and returns `self` for chaining.
108    ///
109    /// Nodes are assigned indices in insertion order (0, 1, 2 …).
110    ///
111    /// # Examples
112    ///
113    /// ```
114    /// use graph_core::GraphBuilder;
115    ///
116    /// let b = GraphBuilder::<&str, f64>::new()
117    ///     .node("X")
118    ///     .node("Y");
119    /// ```
120    pub fn node(mut self, data: N) -> Self {
121        self.nodes.push(data);
122        self
123    }
124
125    /// Adds an edge from `from` to `to` with `weight` and returns `self`.
126    ///
127    /// Node indices are the order they were added via [`node`](Self::node).
128    /// Invalid indices will panic at build time.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use graph_core::GraphBuilder;
134    ///
135    /// let b = GraphBuilder::<(), f64>::new()
136    ///     .node(())
137    ///     .node(())
138    ///     .edge(0, 1, 3.0);
139    /// ```
140    pub fn edge(mut self, from: usize, to: usize, weight: W) -> Self {
141        self.edges.push((from, to, weight));
142        self
143    }
144}
145
146// ── Build ─────────────────────────────────────────────────────────────────────
147
148impl<N, W: Clone> GraphBuilder<N, W> {
149    /// Consumes the builder and produces an [`AdjacencyList`].
150    ///
151    /// # Panics
152    ///
153    /// Panics if any edge references a node index that is out of range.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use graph_core::{GraphBuilder, Graph};
159    ///
160    /// let g = GraphBuilder::<&str, f64>::new()
161    ///     .node("A")
162    ///     .node("B")
163    ///     .edge(0, 1, 5.0)
164    ///     .build_adjacency_list();
165    ///
166    /// assert_eq!(g.edge_count(), 1);
167    /// ```
168    pub fn build_adjacency_list(self) -> AdjacencyList<N, W> {
169        let mut g = if self.directed {
170            AdjacencyList::directed()
171        } else {
172            AdjacencyList::undirected()
173        };
174        let mut ids: Vec<NodeId> = Vec::with_capacity(self.nodes.len());
175        for data in self.nodes {
176            ids.push(g.add_node(data));
177        }
178        for (from, to, weight) in self.edges {
179            g.add_edge(ids[from], ids[to], weight)
180                .expect("builder edge references valid nodes");
181        }
182        g
183    }
184
185    /// Consumes the builder and produces an [`AdjacencyMatrix`].
186    ///
187    /// # Panics
188    ///
189    /// Panics if any edge references a node index that is out of range.
190    ///
191    /// # Examples
192    ///
193    /// ```
194    /// use graph_core::{GraphBuilder, Graph};
195    ///
196    /// let g = GraphBuilder::<&str, f64>::new()
197    ///     .node("A")
198    ///     .node("B")
199    ///     .edge(0, 1, 2.0)
200    ///     .build_adjacency_matrix();
201    ///
202    /// assert!(g.contains_edge(0usize.into(), 1usize.into()));
203    /// ```
204    pub fn build_adjacency_matrix(self) -> AdjacencyMatrix<N, W> {
205        let mut g = if self.directed {
206            AdjacencyMatrix::directed()
207        } else {
208            AdjacencyMatrix::undirected()
209        };
210        let mut ids: Vec<NodeId> = Vec::with_capacity(self.nodes.len());
211        for data in self.nodes {
212            ids.push(g.add_node(data));
213        }
214        for (from, to, weight) in self.edges {
215            g.add_edge(ids[from], ids[to], weight)
216                .expect("builder edge references valid nodes");
217        }
218        g
219    }
220}