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}