dbc_rs/nodes/
core.rs

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