dbc_rs/nodes/core.rs
1use super::Nodes;
2use crate::{
3 Error, MAX_NAME_SIZE, MAX_NODES, Result,
4 compat::{String, Vec},
5 error::check_max_limit,
6};
7
8impl Nodes {
9 // Shared validation function
10 pub(crate) fn validate(nodes: &[impl AsRef<str>]) -> Result<()> {
11 // Check for too many nodes (DoS protection)
12 if let Some(err) = check_max_limit(
13 nodes.len(),
14 MAX_NODES,
15 Error::Validation(Error::NODES_TOO_MANY),
16 ) {
17 return Err(err);
18 }
19
20 // Check for duplicate node names (case-sensitive)
21 for (i, node1) in nodes.iter().enumerate() {
22 for node2 in nodes.iter().skip(i + 1) {
23 if node1.as_ref() == node2.as_ref() {
24 return Err(Error::Validation(Error::NODES_DUPLICATE_NAME));
25 }
26 }
27 }
28 Ok(())
29 }
30
31 pub(crate) fn new(nodes: Vec<String<{ MAX_NAME_SIZE }>, { MAX_NODES }>) -> Self {
32 // Validation should have been done prior (by builder)
33 Self { nodes }
34 }
35
36 /// Returns an iterator over the node names.
37 ///
38 /// # Examples
39 ///
40 /// ```rust,no_run
41 /// use dbc_rs::Dbc;
42 ///
43 /// let dbc = Dbc::parse(r#"VERSION "1.0"
44 ///
45 /// BU_: ECM TCM BCM
46 /// "#)?;
47 ///
48 /// // Iterate over nodes
49 /// let mut iter = dbc.nodes().iter();
50 /// assert_eq!(iter.next(), Some("ECM"));
51 /// assert_eq!(iter.next(), Some("TCM"));
52 /// assert_eq!(iter.next(), Some("BCM"));
53 /// assert_eq!(iter.next(), None);
54 ///
55 /// // Or use in a loop
56 /// for node in dbc.nodes().iter() {
57 /// println!("Node: {}", node);
58 /// }
59 /// # Ok::<(), dbc_rs::Error>(())
60 /// ```
61 #[inline]
62 #[must_use = "iterator is lazy and does nothing unless consumed"]
63 pub fn iter(&self) -> impl Iterator<Item = &str> + '_ {
64 self.nodes.iter().map(|s| s.as_str())
65 }
66
67 /// Checks if a node name is in the list.
68 ///
69 /// The check is case-sensitive.
70 ///
71 /// # Arguments
72 ///
73 /// * `node` - The node name to check
74 ///
75 /// # Examples
76 ///
77 /// ```rust,no_run
78 /// use dbc_rs::Dbc;
79 ///
80 /// let dbc = Dbc::parse(r#"VERSION "1.0"
81 ///
82 /// BU_: ECM TCM
83 /// "#)?;
84 ///
85 /// assert!(dbc.nodes().contains("ECM"));
86 /// assert!(dbc.nodes().contains("TCM"));
87 /// assert!(!dbc.nodes().contains("BCM"));
88 /// assert!(!dbc.nodes().contains("ecm")); // Case-sensitive
89 /// # Ok::<(), dbc_rs::Error>(())
90 /// ```
91 #[inline]
92 #[must_use = "return value should be used"]
93 pub fn contains(&self, node: &str) -> bool {
94 self.iter().any(|n| n == node)
95 }
96
97 /// Returns the number of nodes in the collection.
98 ///
99 /// # Examples
100 ///
101 /// ```rust,no_run
102 /// use dbc_rs::Dbc;
103 ///
104 /// let dbc = Dbc::parse(r#"VERSION "1.0"
105 ///
106 /// BU_: ECM TCM BCM
107 /// "#)?;
108 ///
109 /// assert_eq!(dbc.nodes().len(), 3);
110 /// # Ok::<(), dbc_rs::Error>(())
111 /// ```
112 #[inline]
113 #[must_use = "return value should be used"]
114 pub fn len(&self) -> usize {
115 self.nodes.len()
116 }
117
118 /// Returns `true` if there are no nodes in the collection.
119 ///
120 /// # Examples
121 ///
122 /// ```rust,no_run
123 /// use dbc_rs::Dbc;
124 ///
125 /// // Empty node list
126 /// let dbc = Dbc::parse(r#"VERSION "1.0"
127 ///
128 /// BU_:
129 /// "#)?;
130 /// assert!(dbc.nodes().is_empty());
131 ///
132 /// // With nodes
133 /// let dbc2 = Dbc::parse(r#"VERSION "1.0"
134 ///
135 /// BU_: ECM
136 /// "#)?;
137 /// assert!(!dbc2.nodes().is_empty());
138 /// # Ok::<(), dbc_rs::Error>(())
139 /// ```
140 pub fn is_empty(&self) -> bool {
141 self.nodes.is_empty()
142 }
143
144 /// Gets a node by index.
145 ///
146 /// Returns `None` if the index is out of bounds.
147 ///
148 /// # Arguments
149 ///
150 /// * `index` - The zero-based index of the node
151 ///
152 /// # Examples
153 ///
154 /// ```rust,no_run
155 /// use dbc_rs::Dbc;
156 ///
157 /// let dbc = Dbc::parse(r#"VERSION "1.0"
158 ///
159 /// BU_: ECM TCM BCM
160 /// "#)?;
161 ///
162 /// assert_eq!(dbc.nodes().at(0), Some("ECM"));
163 /// assert_eq!(dbc.nodes().at(1), Some("TCM"));
164 /// assert_eq!(dbc.nodes().at(2), Some("BCM"));
165 /// assert_eq!(dbc.nodes().at(3), None); // Out of bounds
166 /// # Ok::<(), dbc_rs::Error>(())
167 /// ```
168 #[inline]
169 #[must_use = "return value should be used"]
170 pub fn at(&self, index: usize) -> Option<&str> {
171 self.nodes.get(index).map(|s| s.as_str())
172 }
173}