1use crate::{Error, error::messages};
2use alloc::{boxed::Box, string::String, vec::Vec};
3
4#[derive(Debug)]
22pub struct Nodes {
23 nodes: Vec<Box<str>>,
24}
25
26impl Nodes {
27 pub fn builder() -> NodesBuilder {
44 NodesBuilder::new()
45 }
46
47 #[allow(dead_code)] pub(crate) fn new<I, S>(nodes: I) -> Result<Self, Error>
50 where
51 I: IntoIterator<Item = S>,
52 S: AsRef<str>,
53 {
54 let nodes: Vec<Box<str>> = nodes.into_iter().map(|s| s.as_ref().into()).collect();
55 Self::validate(&nodes)?;
56 Ok(Self { nodes })
57 }
58
59 fn validate(nodes: &[Box<str>]) -> Result<(), Error> {
66 for (i, node1) in nodes.iter().enumerate() {
68 for node2 in nodes.iter().skip(i + 1) {
69 if node1.as_ref() == node2.as_ref() {
70 return Err(Error::Nodes(messages::duplicate_node_name(node1.as_ref())));
71 }
72 }
73 }
74 Ok(())
75 }
76
77 pub(super) fn parse(nodes: &str) -> Result<Self, Error> {
78 let nodes: Vec<Box<str>> = nodes[4..].split_whitespace().map(|s| s.into()).collect();
79 Self::validate(&nodes)?;
80 Ok(Self { nodes })
81 }
82
83 #[inline]
87 pub fn nodes(&self) -> Option<&[Box<str>]> {
88 if self.nodes.is_empty() {
89 None
90 } else {
91 Some(self.nodes.as_ref())
92 }
93 }
94
95 #[inline]
97 pub fn contains(&self, node: &str) -> bool {
98 self.nodes.iter().any(|n| n.as_ref() == node)
99 }
100
101 #[allow(clippy::inherent_to_string)]
103 pub fn to_string(&self) -> String {
104 if self.nodes.is_empty() {
105 return String::new();
106 }
107 let capacity = self.nodes.len() * 10;
109 let mut result = String::with_capacity(capacity);
110 for (i, node) in self.nodes.iter().enumerate() {
111 if i > 0 {
112 result.push(' ');
113 }
114 result.push_str(node.as_ref());
115 }
116 result
117 }
118
119 #[inline]
121 pub fn is_empty(&self) -> bool {
122 self.nodes.is_empty()
123 }
124
125 pub fn to_dbc_string(&self) -> String {
142 let mut result = String::from("BU_:");
143 let nodes_str = self.to_string();
144 if !nodes_str.is_empty() {
145 result.push(' ');
146 result.push_str(&nodes_str);
147 }
148 result
149 }
150}
151
152#[derive(Debug)]
174pub struct NodesBuilder {
175 nodes: Vec<Box<str>>,
176}
177
178impl NodesBuilder {
179 fn new() -> Self {
180 Self { nodes: Vec::new() }
181 }
182
183 pub fn add_node(mut self, node: impl AsRef<str>) -> Self {
185 self.nodes.push(node.as_ref().into());
186 self
187 }
188
189 pub fn add_nodes<I, S>(mut self, nodes: I) -> Self
191 where
192 I: IntoIterator<Item = S>,
193 S: AsRef<str>,
194 {
195 self.nodes.extend(nodes.into_iter().map(|s| s.as_ref().into()));
196 self
197 }
198
199 pub fn clear(mut self) -> Self {
201 self.nodes.clear();
202 self
203 }
204
205 pub fn validate(&self) -> Result<(), Error> {
215 Nodes::validate(&self.nodes)
216 }
217
218 pub fn build(self) -> Result<Nodes, Error> {
225 Nodes::validate(&self.nodes)?;
226 Ok(Nodes { nodes: self.nodes })
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use crate::error::lang;
234
235 #[test]
236 fn test_nodes_from_valid_line() {
237 let line = "BU_: ECM TCM BCM ABS";
238 let nodes = Nodes::parse(line).unwrap();
239 let node_slice = nodes.nodes().unwrap();
240 assert_eq!(
241 node_slice,
242 &["ECM".into(), "TCM".into(), "BCM".into(), "ABS".into()]
243 );
244 }
245
246 #[test]
247 fn test_nodes_from_single_node() {
248 let line = "BU_: ONLYONE";
249 let nodes = Nodes::parse(line).unwrap();
250 let node_slice = nodes.nodes().unwrap();
251 assert_eq!(node_slice, &["ONLYONE".into()]);
252 }
253
254 #[test]
255 fn test_nodes_from_with_extra_spaces() {
256 let line = "BU_: Node1 Node2 ";
257 let nodes = Nodes::parse(line).unwrap();
258 let node_slice = nodes.nodes().unwrap();
259 assert_eq!(node_slice, &["Node1".into(), "Node2".into()]);
260 }
261
262 #[test]
263 fn test_nodes_from_empty_list() {
264 let line = "BU_:";
265 let nodes = Nodes::parse(line).unwrap();
266 assert!(nodes.nodes().is_none());
267 }
268
269 #[test]
270 fn test_nodes_new() {
271 let nodes = Nodes::new(["ECM", "TCM", "BCM"]).unwrap();
272 assert!(nodes.contains("ECM"));
273 assert!(nodes.contains("TCM"));
274 assert!(nodes.contains("BCM"));
275 assert!(!nodes.contains("ABS"));
276 assert_eq!(nodes.nodes().unwrap().len(), 3);
277 }
278
279 #[test]
280 fn test_nodes_new_from_vec() {
281 let node_vec = vec!["Node1", "Node2", "Node3"];
282 let nodes = Nodes::new(node_vec).unwrap();
283 assert!(nodes.contains("Node1"));
284 assert_eq!(nodes.nodes().unwrap().len(), 3);
285 }
286
287 #[test]
288 fn test_nodes_new_from_slice() {
289 let node_slice = &["A", "B", "C"][..];
290 let nodes = Nodes::new(node_slice).unwrap();
291 assert!(nodes.contains("A"));
292 assert_eq!(nodes.nodes().unwrap().len(), 3);
293 }
294
295 #[test]
296 fn test_nodes_new_duplicate() {
297 let result = Nodes::new(["ECM", "TCM", "ECM"]);
298 assert!(result.is_err());
299 match result.unwrap_err() {
300 Error::Nodes(msg) => assert!(msg.contains(lang::NODES_DUPLICATE_NAME)),
301 _ => panic!("Expected Nodes error"),
302 }
303 }
304
305 #[test]
306 fn test_nodes_to_string_single() {
307 let nodes = Nodes::new(["ECM"]).unwrap();
308 assert_eq!(nodes.to_string(), "ECM");
309 }
310
311 #[test]
312 fn test_nodes_to_string_multiple() {
313 let nodes = Nodes::new(["ECM", "TCM", "BCM"]).unwrap();
314 assert_eq!(nodes.to_string(), "ECM TCM BCM");
315 }
316
317 #[test]
318 fn test_nodes_to_dbc_string() {
319 let nodes_single = Nodes::new(["ECM"]).unwrap();
320 assert_eq!(nodes_single.to_dbc_string(), "BU_: ECM");
321
322 let nodes_multiple = Nodes::new(["ECM", "TCM", "BCM"]).unwrap();
323 assert_eq!(nodes_multiple.to_dbc_string(), "BU_: ECM TCM BCM");
324 }
325
326 #[test]
327 fn test_nodes_builder_duplicate() {
328 let result = Nodes::builder().add_node("ECM").add_node("TCM").add_node("ECM").build();
329 assert!(result.is_err());
330 match result.unwrap_err() {
331 Error::Nodes(msg) => assert!(msg.contains(lang::NODES_DUPLICATE_NAME)),
332 _ => panic!("Expected Nodes error"),
333 }
334 }
335
336 #[test]
337 fn test_nodes_parse_duplicate() {
338 let line = "BU_: ECM TCM ECM";
339 let result = Nodes::parse(line);
340 assert!(result.is_err());
341 match result.unwrap_err() {
342 Error::Nodes(msg) => assert!(msg.contains(lang::NODES_DUPLICATE_NAME)),
343 _ => panic!("Expected Nodes error"),
344 }
345 }
346}