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