dbc_rs/nodes/builder/
build.rs

1use super::NodesBuilder;
2use crate::nodes::{InnerNodes, Node};
3use crate::{Error, MAX_NODES, Nodes, Result};
4
5impl NodesBuilder {
6    /// Convert std strings to compat Node structs and validate
7    fn to_compat_nodes(&self) -> Result<InnerNodes> {
8        use crate::compat::{Comment, Name, Vec};
9        let mut result: InnerNodes = InnerNodes::new();
10        let mut names: Vec<Name, { MAX_NODES }> = Vec::new();
11        for (node_name, node_comment) in &self.nodes {
12            let compat_name = Name::try_from(node_name.as_str())
13                .map_err(|_| Error::Validation(Error::MAX_NAME_SIZE_EXCEEDED))?;
14            let _ = names.push(compat_name.clone());
15
16            let compat_comment = if let Some(comment) = node_comment {
17                Some(
18                    Comment::try_from(comment.as_str())
19                        .map_err(|_| Error::Validation("Comment exceeds maximum length"))?,
20                )
21            } else {
22                None
23            };
24
25            result
26                .push(Node::with_comment(compat_name, compat_comment))
27                .map_err(|_| Error::Validation(Error::NODES_TOO_MANY))?;
28        }
29        Nodes::validate(&names)?;
30        Ok(result)
31    }
32
33    /// Validates the current builder state without building.
34    ///
35    /// This is useful for checking if the configuration is valid before building.
36    /// Returns a new builder with validated nodes, or an error if validation fails.
37    ///
38    /// # Returns
39    ///
40    /// Returns `Ok(Self)` if validation succeeds, or `Err(Error::Validation)` if:
41    /// - Too many nodes are specified (exceeds 256 nodes  limit by default)
42    /// - Duplicate node names are found (case-sensitive)
43    /// - Node name exceeds maximum length (32 characters by default)
44    ///
45    /// # Examples
46    ///
47    /// ```rust,no_run
48    /// use dbc_rs::NodesBuilder;
49    ///
50    /// // Valid configuration
51    /// let builder = NodesBuilder::new()
52    ///     .add_node("ECM")
53    ///     .add_node("TCM");
54    /// assert!(builder.validate().is_ok());
55    ///
56    /// // Invalid: duplicate nodes
57    /// let builder2 = NodesBuilder::new()
58    ///     .add_node("ECM")
59    ///     .add_node("ECM"); // Duplicate
60    /// assert!(builder2.validate().is_err());
61    /// # Ok::<(), dbc_rs::Error>(())
62    /// ```
63    #[must_use = "validation result should be checked"]
64    pub fn validate(self) -> Result<Self> {
65        self.to_compat_nodes()?;
66        Ok(self)
67    }
68
69    /// Builds the `Nodes` from the builder configuration.
70    ///
71    /// This validates the nodes and constructs a `Nodes` instance.
72    ///
73    /// # Returns
74    ///
75    /// Returns `Ok(Nodes)` if successful, or `Err(Error::Validation)` if:
76    /// - Too many nodes are specified (exceeds 256 nodes limit by default)
77    /// - Duplicate node names are found (case-sensitive)
78    /// - Node name exceeds maximum length (32 characters)
79    ///
80    /// # Examples
81    ///
82    /// ```rust,no_run
83    /// use dbc_rs::NodesBuilder;
84    ///
85    /// // Build with nodes
86    /// let nodes = NodesBuilder::new()
87    ///     .add_node("ECM")
88    ///     .add_node("TCM")
89    ///     .build()?;
90    /// assert_eq!(nodes.len(), 2);
91    ///
92    /// // Build empty
93    /// let empty = NodesBuilder::new().build()?;
94    /// assert!(empty.is_empty());
95    /// # Ok::<(), dbc_rs::Error>(())
96    /// ```
97    ///
98    /// # Errors
99    ///
100    /// ```rust,no_run
101    /// use dbc_rs::NodesBuilder;
102    ///
103    /// // Duplicate nodes
104    /// let result = NodesBuilder::new()
105    ///     .add_node("ECM")
106    ///     .add_node("ECM") // Duplicate
107    ///     .build();
108    /// assert!(result.is_err());
109    /// # Ok::<(), dbc_rs::Error>(())
110    /// ```
111    pub fn build(self) -> Result<Nodes> {
112        let compat_nodes = self.to_compat_nodes()?;
113        Ok(Nodes::new(compat_nodes))
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    #![allow(clippy::float_cmp)]
120    use super::*;
121    use crate::Error;
122
123    #[test]
124    fn test_nodes_builder_empty() {
125        let nodes = NodesBuilder::new().build().unwrap();
126        assert!(nodes.is_empty());
127        assert_eq!(nodes.len(), 0);
128    }
129
130    #[test]
131    fn test_nodes_builder_duplicate() {
132        let result = NodesBuilder::new().add_node("ECM").add_node("TCM").add_node("ECM").build();
133        assert!(result.is_err());
134        match result.unwrap_err() {
135            Error::Validation(msg) => assert!(msg.contains(Error::NODES_DUPLICATE_NAME)),
136            _ => panic!("Expected Validation error"),
137        }
138    }
139
140    #[test]
141    fn test_nodes_builder_too_many() {
142        let mut builder = NodesBuilder::new();
143        for i in 0..MAX_NODES {
144            let node_str = format!("Node{i}");
145            builder = builder.add_node(node_str);
146        }
147        let node = "NodeLast".to_string();
148        let result = builder.add_node(node).build();
149        assert!(result.is_err());
150        match result.unwrap_err() {
151            Error::Validation(msg) => {
152                assert!(msg.contains(Error::NODES_TOO_MANY));
153            }
154            _ => panic!("Expected Validation error"),
155        }
156    }
157
158    #[test]
159    fn test_nodes_builder_validate() {
160        let builder = NodesBuilder::new().add_node("TCM").add_node("BCM");
161        let validated = builder.validate().unwrap();
162        let nodes = validated.build().unwrap();
163        assert_eq!(nodes.len(), 2);
164    }
165
166    #[test]
167    fn test_nodes_builder_validate_duplicate() {
168        let result = NodesBuilder::new().add_node("ECM").add_node("TCM").add_node("ECM").build();
169        assert!(result.is_err());
170        match result.unwrap_err() {
171            Error::Validation(msg) => assert!(msg.contains(Error::NODES_DUPLICATE_NAME)),
172            _ => panic!("Expected Validation error"),
173        }
174    }
175
176    #[test]
177    fn test_nodes_builder_validate_too_many() {
178        let mut builder = NodesBuilder::new();
179        for i in 0..MAX_NODES {
180            let node_str = format!("Node{i}");
181            builder = builder.add_node(node_str);
182        }
183
184        let result = builder.validate();
185        assert!(result.is_ok());
186
187        // Try to adding one past the limit
188        builder = result.unwrap();
189        let result = builder.add_node("NodeLast").build();
190        assert!(result.is_err());
191        match result.unwrap_err() {
192            Error::Validation(msg) => assert!(msg.contains(Error::NODES_TOO_MANY)),
193            _ => panic!("Expected Validation error"),
194        }
195    }
196}