dbc_rs/nodes/
nodes_builder.rs

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