dbc_rs/nodes/
nodes_builder.rs

1use crate::{error::Error, error::Result, nodes::Nodes};
2
3#[derive(Debug, Default)]
4pub struct NodesBuilder {
5    nodes: Vec<String>,
6}
7
8impl NodesBuilder {
9    pub fn new() -> Self {
10        Self::default()
11    }
12
13    #[must_use]
14    pub fn add_node(mut self, node: impl AsRef<str>) -> Self {
15        self.nodes.push(node.as_ref().to_string());
16        self
17    }
18
19    #[must_use]
20    pub fn add_nodes<I, S>(mut self, nodes: I) -> Self
21    where
22        I: IntoIterator<Item = S>,
23        S: AsRef<str>,
24    {
25        self.nodes.extend(nodes.into_iter().map(|s| s.as_ref().to_string()));
26        self
27    }
28
29    #[must_use]
30    pub fn clear(mut self) -> Self {
31        self.nodes.clear();
32        self
33    }
34
35    fn extract_and_validate_nodes(self) -> Result<Vec<String>> {
36        let node_strs: Vec<String> = self.nodes.into_iter().map(|s| s.to_string()).collect();
37        let node_refs: Vec<&str> = node_strs.iter().map(|s| s.as_str()).collect();
38        super::Nodes::validate_nodes(&node_refs).map_err(|e| match e {
39            crate::error::ParseError::Version(msg) => Error::Nodes(String::from(msg)),
40            _ => Error::from(e),
41        })?;
42        Ok(node_strs)
43    }
44
45    #[must_use = "validation result should be checked"]
46    pub fn validate(self) -> Result<Self> {
47        let node_strs = self.extract_and_validate_nodes()?;
48        Ok(Self { nodes: node_strs })
49    }
50
51    pub fn build(self) -> Result<Nodes<'static>> {
52        let node_strs = self.extract_and_validate_nodes()?;
53        // Convert owned Strings to static references by leaking Box<str>
54        // This is acceptable for builder pattern where the caller owns the data
55        let mut node_refs: Vec<&'static str> = Vec::new();
56        for s in node_strs {
57            let boxed: Box<str> = s.into_boxed_str();
58            node_refs.push(Box::leak(boxed));
59        }
60        // Validate before construction
61        super::Nodes::validate_nodes(&node_refs).map_err(|e| match e {
62            crate::error::ParseError::Version(msg) => Error::Nodes(String::from(msg)),
63            _ => Error::from(e),
64        })?;
65        Ok(Nodes::new(&node_refs))
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    #![allow(clippy::float_cmp)]
72    use super::*;
73    use crate::{error::Error, error::lang};
74
75    #[test]
76    fn test_nodes_builder_duplicate() {
77        let result = NodesBuilder::new().add_node("ECM").add_node("TCM").add_node("ECM").build();
78        assert!(result.is_err());
79        match result.unwrap_err() {
80            Error::Nodes(msg) => assert!(msg.contains(lang::NODES_DUPLICATE_NAME)),
81            _ => panic!("Expected Nodes error"),
82        }
83    }
84
85    #[test]
86    fn test_nodes_builder_too_many() {
87        let mut builder = NodesBuilder::new();
88        for i in 0..257 {
89            builder = builder.add_node(format!("Node{i}"));
90        }
91        let result = builder.build();
92        assert!(result.is_err());
93        match result.unwrap_err() {
94            Error::Nodes(msg) => {
95                assert!(msg.contains(lang::NODES_TOO_MANY));
96            }
97            _ => panic!("Expected Nodes error"),
98        }
99    }
100}