dbc_rs/nodes/nodes_builder.rs
1use crate::{Error, MAX_NAME_SIZE, MAX_NODES, Nodes, Result, error::lang};
2use std::{string::String, vec::Vec};
3
4/// Builder for creating `Nodes` programmatically.
5///
6/// This builder allows you to construct node lists when building DBC files
7/// programmatically. It validates that node names are unique and within limits.
8///
9/// # Examples
10///
11/// ```rust,no_run
12/// use dbc_rs::NodesBuilder;
13///
14/// let nodes = NodesBuilder::new()
15/// .add_node("ECM")
16/// .add_node("TCM")
17/// .add_node("BCM")
18/// .build()?;
19///
20/// assert_eq!(nodes.len(), 3);
21/// assert!(nodes.contains("ECM"));
22/// # Ok::<(), dbc_rs::Error>(())
23/// ```
24///
25/// # Validation
26///
27/// The builder validates:
28/// - Maximum of 256 nodes (DoS protection)
29/// - No duplicate node names (case-sensitive)
30///
31/// # Feature Requirements
32///
33/// This builder requires the `std` feature to be enabled.
34#[derive(Debug)]
35pub struct NodesBuilder {
36 nodes: Vec<String>,
37}
38
39impl NodesBuilder {
40 /// Creates a new `NodesBuilder` with an empty node list.
41 ///
42 /// # Examples
43 ///
44 /// ```rust,no_run
45 /// use dbc_rs::NodesBuilder;
46 ///
47 /// let builder = NodesBuilder::new();
48 /// let nodes = builder.build()?;
49 /// assert!(nodes.is_empty());
50 /// # Ok::<(), dbc_rs::Error>(())
51 /// ```
52 pub fn new() -> Self {
53 Self { nodes: Vec::new() }
54 }
55
56 /// Adds a single node to the list.
57 ///
58 /// # Arguments
59 ///
60 /// * `node` - The node name (anything that implements `AsRef<str>`)
61 ///
62 /// # Examples
63 ///
64 /// ```rust,no_run
65 /// use dbc_rs::NodesBuilder;
66 ///
67 /// let nodes = NodesBuilder::new()
68 /// .add_node("ECM")
69 /// .add_node("TCM")
70 /// .build()?;
71 /// assert_eq!(nodes.len(), 2);
72 /// # Ok::<(), dbc_rs::Error>(())
73 /// ```
74 #[must_use]
75 pub fn add_node(mut self, node: impl AsRef<str>) -> Self {
76 self.nodes.push(node.as_ref().to_string());
77 self
78 }
79
80 /// Adds multiple nodes from an iterator.
81 ///
82 /// # Arguments
83 ///
84 /// * `nodes` - An iterator of node names (each item must implement `AsRef<str>`)
85 ///
86 /// # Examples
87 ///
88 /// ```rust,no_run
89 /// use dbc_rs::NodesBuilder;
90 ///
91 /// // From a slice
92 /// let nodes = NodesBuilder::new()
93 /// .add_nodes(&["ECM", "TCM", "BCM"])
94 /// .build()?;
95 /// assert_eq!(nodes.len(), 3);
96 ///
97 /// // From a vector
98 /// let node_vec = vec!["Node1", "Node2"];
99 /// let nodes2 = NodesBuilder::new()
100 /// .add_nodes(node_vec.iter())
101 /// .build()?;
102 /// assert_eq!(nodes2.len(), 2);
103 /// # Ok::<(), dbc_rs::Error>(())
104 /// ```
105 #[must_use]
106 pub fn add_nodes<I, S>(mut self, nodes: I) -> Self
107 where
108 I: IntoIterator<Item = S>,
109 S: AsRef<str>,
110 {
111 for node in nodes {
112 self = self.add_node(node.as_ref());
113 }
114
115 self
116 }
117
118 /// Clears all nodes from the builder.
119 ///
120 /// # Examples
121 ///
122 /// ```rust,no_run
123 /// use dbc_rs::NodesBuilder;
124 ///
125 /// let nodes = NodesBuilder::new()
126 /// .add_node("ECM")
127 /// .add_node("TCM")
128 /// .clear()
129 /// .add_node("BCM")
130 /// .build()?;
131 /// assert_eq!(nodes.len(), 1);
132 /// assert!(nodes.contains("BCM"));
133 /// assert!(!nodes.contains("ECM"));
134 /// # Ok::<(), dbc_rs::Error>(())
135 /// ```
136 #[must_use]
137 pub fn clear(mut self) -> Self {
138 self.nodes.clear();
139 self
140 }
141
142 fn extract_and_validate_nodes(self) -> Result<Vec<String>> {
143 Nodes::validate(&self.nodes)?;
144 Ok(self.nodes)
145 }
146
147 /// Validates the current builder state without building.
148 ///
149 /// This is useful for checking if the configuration is valid before building.
150 /// Returns a new builder with validated nodes, or an error if validation fails.
151 ///
152 /// # Returns
153 ///
154 /// Returns `Ok(Self)` if validation succeeds, or `Err(Error::Validation)` if:
155 /// - More than 256 nodes are specified (exceeds maximum limit)
156 /// - Duplicate node names are found (case-sensitive)
157 ///
158 /// # Examples
159 ///
160 /// ```rust,no_run
161 /// use dbc_rs::NodesBuilder;
162 ///
163 /// // Valid configuration
164 /// let builder = NodesBuilder::new()
165 /// .add_node("ECM")
166 /// .add_node("TCM");
167 /// assert!(builder.validate().is_ok());
168 ///
169 /// // Invalid: duplicate nodes
170 /// let builder2 = NodesBuilder::new()
171 /// .add_node("ECM")
172 /// .add_node("ECM"); // Duplicate
173 /// assert!(builder2.validate().is_err());
174 /// # Ok::<(), dbc_rs::Error>(())
175 /// ```
176 #[must_use = "validation result should be checked"]
177 pub fn validate(self) -> Result<Self> {
178 let nodes = self.extract_and_validate_nodes()?;
179 Ok(Self { nodes })
180 }
181
182 /// Builds the `Nodes` from the builder configuration.
183 ///
184 /// This validates the nodes and constructs a `Nodes` instance.
185 ///
186 /// # Returns
187 ///
188 /// Returns `Ok(Nodes)` if successful, or `Err(Error::Validation)` if:
189 /// - More than 256 nodes are specified (exceeds maximum limit)
190 /// - Duplicate node names are found (case-sensitive)
191 ///
192 /// # Examples
193 ///
194 /// ```rust,no_run
195 /// use dbc_rs::NodesBuilder;
196 ///
197 /// // Build with nodes
198 /// let nodes = NodesBuilder::new()
199 /// .add_node("ECM")
200 /// .add_node("TCM")
201 /// .build()?;
202 /// assert_eq!(nodes.len(), 2);
203 ///
204 /// // Build empty
205 /// let empty = NodesBuilder::new().build()?;
206 /// assert!(empty.is_empty());
207 /// # Ok::<(), dbc_rs::Error>(())
208 /// ```
209 ///
210 /// # Errors
211 ///
212 /// ```rust,no_run
213 /// use dbc_rs::NodesBuilder;
214 ///
215 /// // Duplicate nodes
216 /// let result = NodesBuilder::new()
217 /// .add_node("ECM")
218 /// .add_node("ECM") // Duplicate
219 /// .build();
220 /// assert!(result.is_err());
221 /// # Ok::<(), dbc_rs::Error>(())
222 /// ```
223 pub fn build(self) -> Result<Nodes> {
224 let nodes = self.extract_and_validate_nodes()?;
225 // Convert std::vec::Vec<String> to compat::Vec<String<MAX_NAME_SIZE>, MAX_NODES>
226 use crate::compat::{String, Vec};
227 let mut result: Vec<String<{ MAX_NAME_SIZE }>, { MAX_NODES }> = Vec::new();
228 for node_str in nodes {
229 let compat_str = String::try_from(node_str.as_str())
230 .map_err(|_| Error::Validation(lang::MAX_NAME_SIZE_EXCEEDED))?;
231 result.push(compat_str).map_err(|_| Error::Validation(lang::NODES_TOO_MANY))?;
232 }
233 Ok(Nodes::new(result.as_slice()))
234 }
235}
236
237impl Default for NodesBuilder {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 #![allow(clippy::float_cmp)]
246 use super::*;
247 use crate::{Error, error::lang};
248
249 #[test]
250 fn test_nodes_builder_duplicate() {
251 let result = NodesBuilder::new().add_node("ECM").add_node("TCM").add_node("ECM").build();
252 assert!(result.is_err());
253 match result.unwrap_err() {
254 Error::Validation(msg) => assert!(msg.contains(lang::NODES_DUPLICATE_NAME)),
255 _ => panic!("Expected Validation error"),
256 }
257 }
258
259 #[test]
260 fn test_nodes_builder_too_many() {
261 let mut builder = NodesBuilder::new();
262 for i in 0..MAX_NODES {
263 let node_str = format!("Node{i}");
264 builder = builder.add_node(node_str);
265 }
266 let node = "NodeLast".to_string();
267 let result = builder.add_node(node).build();
268 assert!(result.is_err());
269 match result.unwrap_err() {
270 Error::Validation(msg) => {
271 assert!(msg.contains(lang::NODES_TOO_MANY));
272 }
273 _ => panic!("Expected Validation error"),
274 }
275 }
276
277 #[test]
278 fn test_nodes_builder_add_nodes() {
279 let nodes = NodesBuilder::new().add_nodes(["ECM", "TCM", "BCM"]).build().unwrap();
280 assert_eq!(nodes.len(), 3);
281 assert!(nodes.contains("ECM"));
282 assert!(nodes.contains("TCM"));
283 assert!(nodes.contains("BCM"));
284 }
285
286 #[test]
287 fn test_nodes_builder_add_nodes_iterator() {
288 let node_vec = ["Node1", "Node2", "Node3"];
289 let nodes = NodesBuilder::new().add_nodes(node_vec.iter()).build().unwrap();
290 assert_eq!(nodes.len(), 3);
291 assert!(nodes.contains("Node1"));
292 }
293
294 #[test]
295 fn test_nodes_builder_clear() {
296 let nodes = NodesBuilder::new()
297 .add_node("ECM")
298 .add_node("TCM")
299 .clear()
300 .add_node("BCM")
301 .build()
302 .unwrap();
303 assert_eq!(nodes.len(), 1);
304 assert!(nodes.contains("BCM"));
305 assert!(!nodes.contains("ECM"));
306 assert!(!nodes.contains("TCM"));
307 }
308
309 #[test]
310 fn test_nodes_builder_validate() {
311 let builder = NodesBuilder::new().add_node("TCM").add_node("BCM");
312 let validated = builder.validate().unwrap();
313 let nodes = validated.build().unwrap();
314 assert_eq!(nodes.len(), 2);
315 }
316
317 #[test]
318 fn test_nodes_builder_validate_duplicate() {
319 let result = NodesBuilder::new().add_node("ECM").add_node("TCM").add_node("ECM").build();
320 assert!(result.is_err());
321 match result.unwrap_err() {
322 Error::Validation(msg) => assert!(msg.contains(lang::NODES_DUPLICATE_NAME)),
323 _ => panic!("Expected Validation error"),
324 }
325 }
326
327 #[test]
328 fn test_nodes_builder_validate_too_many() {
329 let mut builder = NodesBuilder::new();
330 for i in 0..MAX_NODES {
331 let node_str = format!("Node{i}");
332 builder = builder.add_node(node_str);
333 }
334
335 let result = builder.validate();
336 assert!(result.is_ok());
337
338 // Try to adding one past the limit
339 builder = result.unwrap();
340 let result = builder.add_node("NodeLast").build();
341 assert!(result.is_err());
342 match result.unwrap_err() {
343 Error::Validation(msg) => assert!(msg.contains(lang::NODES_TOO_MANY)),
344 _ => panic!("Expected Validation error"),
345 }
346 }
347
348 #[test]
349 fn test_nodes_builder_empty() {
350 let nodes = NodesBuilder::new().build().unwrap();
351 assert!(nodes.is_empty());
352 assert_eq!(nodes.len(), 0);
353 }
354}