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}