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}