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}