dbc_rs/receivers/
receivers.rs

1use crate::{
2    Error, MAX_NAME_SIZE, MAX_RECEIVER_NODES, Parser, Result,
3    compat::{String, Vec},
4    error::lang,
5};
6
7/// Represents the receiver nodes for a signal in a DBC file.
8///
9/// A signal can have three types of receivers:
10/// - **Broadcast** (`*`): The signal is broadcast to all nodes on the bus
11/// - **Specific nodes**: A list of specific node names that receive this signal
12/// - **None**: No explicit receivers specified (signal may be unused or receiver is implicit)
13///
14/// # Examples
15///
16/// ```rust,no_run
17/// use dbc_rs::Dbc;
18///
19/// let dbc = Dbc::parse(r#"VERSION "1.0"
20///
21/// BU_: ECM TCM BCM
22///
23/// BO_ 256 Engine : 8 ECM
24///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
25///  SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C" TCM BCM
26/// "#)?;
27///
28/// let message = dbc.messages().at(0).unwrap();
29///
30/// // Broadcast receiver
31/// let rpm_signal = message.signals().find("RPM").unwrap();
32/// assert_eq!(rpm_signal.receivers().len(), 0); // Broadcast has no specific nodes
33///
34/// // Specific nodes
35/// let temp_signal = message.signals().find("Temp").unwrap();
36/// assert_eq!(temp_signal.receivers().len(), 2);
37/// assert!(temp_signal.receivers().contains("TCM"));
38/// # Ok::<(), dbc_rs::Error>(())
39/// ```
40///
41/// # DBC Format
42///
43/// In DBC files, receivers are specified after the signal definition:
44/// - `*` indicates broadcast
45/// - Space-separated node names indicate specific receivers
46/// - No receivers means `None`
47#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48#[allow(clippy::large_enum_variant)]
49pub enum Receivers {
50    /// Broadcast receiver - signal is sent to all nodes on the bus.
51    Broadcast,
52    /// Specific receiver nodes - vector of node names.
53    Nodes(Vec<String<{ MAX_NAME_SIZE }>, { MAX_RECEIVER_NODES }>),
54    /// No explicit receivers specified.
55    None,
56}
57
58impl Receivers {
59    pub(crate) fn new_broadcast() -> Self {
60        Receivers::Broadcast
61    }
62
63    pub(crate) fn new_none() -> Self {
64        Receivers::None
65    }
66
67    #[cfg(feature = "std")]
68    pub(crate) fn new_nodes(nodes: &[String<{ MAX_NAME_SIZE }>]) -> Self {
69        // Validation should have been done prior (by builder or parse)
70        let vec_nodes: Vec<String<{ MAX_NAME_SIZE }>, { MAX_RECEIVER_NODES }> =
71            nodes.iter().take(MAX_RECEIVER_NODES).cloned().collect();
72        Receivers::Nodes(vec_nodes)
73    }
74
75    pub(crate) fn parse(parser: &mut Parser) -> Result<Self> {
76        // Skip any leading spaces (but not newlines - newlines indicate end of line)
77        // If we get UnexpectedEof, we're at EOF, so return None
78        match parser.skip_whitespace() {
79            Ok(_) => {}
80            Err(Error::UnexpectedEof) => return Ok(Self::new_none()),
81            Err(_) => {} // Other errors (like Expected) mean there's no whitespace, continue
82        }
83
84        // Check if next character is '*' (broadcast marker)
85        if parser.expect(b"*").is_ok() {
86            return Ok(Self::new_broadcast());
87        }
88
89        // Check if we're at a newline (end of signal line)
90        if parser.expect(b"\n").is_ok() || parser.expect(b"\r").is_ok() {
91            return Ok(Self::new_none());
92        }
93
94        // Parse space-separated identifiers into Vec
95        let mut nodes: Vec<String<{ MAX_NAME_SIZE }>, { MAX_RECEIVER_NODES }> = Vec::new();
96
97        loop {
98            // Skip spaces (but not newlines)
99            // If we get UnexpectedEof, we're at EOF, so break
100            match parser.skip_whitespace() {
101                Ok(_) => {}
102                Err(Error::UnexpectedEof) => break,
103                Err(_) => {} // Other errors mean there's no whitespace, continue
104            }
105
106            // Check if we're at a newline (end of signal line)
107            if parser.expect(b"\n").is_ok() || parser.expect(b"\r").is_ok() {
108                break;
109            }
110
111            // Try to parse an identifier
112            // parse_identifier() stops at newlines without consuming them
113            let pos_before = parser.pos();
114            match parser.parse_identifier() {
115                Ok(node) => {
116                    if let Some(err) = crate::check_max_limit(
117                        nodes.len(),
118                        MAX_RECEIVER_NODES - 1,
119                        Error::Receivers(lang::SIGNAL_RECEIVERS_TOO_MANY),
120                    ) {
121                        return Err(err);
122                    }
123                    let node = crate::validate_name(node)?;
124                    nodes
125                        .push(node)
126                        .map_err(|_| Error::Receivers(lang::SIGNAL_RECEIVERS_TOO_MANY))?;
127                }
128                Err(Error::UnexpectedEof) => break,
129                Err(_) => {
130                    // Failed to parse - if position didn't change, we're at newline or invalid char
131                    if parser.pos() == pos_before {
132                        break;
133                    }
134                    // Position changed but parsing failed - invalid character, also break
135                    break;
136                }
137            }
138        }
139
140        if nodes.is_empty() {
141            Ok(Self::new_none())
142        } else {
143            Ok(Receivers::Nodes(nodes))
144        }
145    }
146
147    /// Returns an iterator over the receiver node names.
148    ///
149    /// For `Receivers::Broadcast` and `Receivers::None`, the iterator will be empty.
150    /// For `Receivers::Nodes`, it iterates over the specific node names.
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    /// BO_ 256 Engine : 8 ECM
162    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
163    /// "#)?;
164    ///
165    /// let message = dbc.messages().at(0).unwrap();
166    /// let signal = message.signals().at(0).unwrap();
167    ///
168    /// // Iterate over receiver nodes
169    /// let mut iter = signal.receivers().iter();
170    /// assert_eq!(iter.next().map(|s| s.to_string()), Some("TCM".to_string()));
171    /// assert_eq!(iter.next().map(|s| s.to_string()), Some("BCM".to_string()));
172    /// assert_eq!(iter.next(), None);
173    /// # Ok::<(), dbc_rs::Error>(())
174    /// ```
175    ///
176    /// # Broadcast and None
177    ///
178    /// ```rust,no_run
179    /// use dbc_rs::Dbc;
180    ///
181    /// let dbc = Dbc::parse(r#"VERSION "1.0"
182    ///
183    /// BU_: ECM
184    ///
185    /// BO_ 256 Engine : 8 ECM
186    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
187    /// "#)?;
188    ///
189    /// let message = dbc.messages().at(0).unwrap();
190    /// let signal = message.signals().at(0).unwrap();
191    ///
192    /// // Broadcast receivers return empty iterator
193    /// assert_eq!(signal.receivers().iter().count(), 0);
194    /// # Ok::<(), dbc_rs::Error>(())
195    /// ```
196    #[inline]
197    #[must_use = "iterator is lazy and does nothing unless consumed"]
198    pub fn iter(&self) -> impl Iterator<Item = String<{ MAX_NAME_SIZE }>> {
199        match self {
200            Receivers::Nodes(nodes) => ReceiversIter {
201                nodes: Some(nodes.clone()),
202                pos: 0,
203            },
204            _ => ReceiversIter {
205                nodes: None,
206                pos: 0,
207            },
208        }
209    }
210
211    /// Returns the number of receiver nodes.
212    ///
213    /// - For `Receivers::Nodes`: Returns the count of specific receiver nodes
214    /// - For `Receivers::Broadcast` and `Receivers::None`: Returns `0`
215    ///
216    /// # Examples
217    ///
218    /// ```rust,no_run
219    /// use dbc_rs::Dbc;
220    ///
221    /// let dbc = Dbc::parse(r#"VERSION "1.0"
222    ///
223    /// BU_: ECM TCM BCM
224    ///
225    /// BO_ 256 Engine : 8 ECM
226    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
227    /// "#)?;
228    ///
229    /// let message = dbc.messages().at(0).unwrap();
230    /// let signal = message.signals().at(0).unwrap();
231    /// assert_eq!(signal.receivers().len(), 2);
232    /// # Ok::<(), dbc_rs::Error>(())
233    /// ```
234    #[inline]
235    #[must_use]
236    pub fn len(&self) -> usize {
237        match self {
238            Receivers::Nodes(nodes) => nodes.len(),
239            Receivers::Broadcast | Receivers::None => 0,
240        }
241    }
242
243    /// Returns `true` if there are no specific receiver nodes.
244    ///
245    /// This returns `true` for both `Receivers::Broadcast` and `Receivers::None`,
246    /// as neither has specific node names.
247    ///
248    /// # Examples
249    ///
250    /// ```rust,no_run
251    /// use dbc_rs::Dbc;
252    ///
253    /// let dbc = Dbc::parse(r#"VERSION "1.0"
254    ///
255    /// BU_: ECM
256    ///
257    /// BO_ 256 Engine : 8 ECM
258    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
259    /// "#)?;
260    ///
261    /// let message = dbc.messages().at(0).unwrap();
262    /// let signal = message.signals().at(0).unwrap();
263    /// assert!(signal.receivers().is_empty());
264    /// # Ok::<(), dbc_rs::Error>(())
265    /// ```
266    #[inline]
267    #[must_use]
268    pub fn is_empty(&self) -> bool {
269        self.len() == 0
270    }
271
272    /// Checks if a node name is in the receivers list.
273    ///
274    /// For `Receivers::Broadcast` and `Receivers::None`, this always returns `false`.
275    /// For `Receivers::Nodes`, it checks if the node name is in the list.
276    ///
277    /// # Arguments
278    ///
279    /// * `node` - The node name to check
280    ///
281    /// # Examples
282    ///
283    /// ```rust,no_run
284    /// use dbc_rs::Dbc;
285    ///
286    /// let dbc = Dbc::parse(r#"VERSION "1.0"
287    ///
288    /// BU_: ECM TCM BCM
289    ///
290    /// BO_ 256 Engine : 8 ECM
291    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
292    /// "#)?;
293    ///
294    /// let message = dbc.messages().at(0).unwrap();
295    /// let signal = message.signals().at(0).unwrap();
296    ///
297    /// assert!(signal.receivers().contains("TCM"));
298    /// assert!(signal.receivers().contains("BCM"));
299    /// assert!(!signal.receivers().contains("ECM"));
300    /// # Ok::<(), dbc_rs::Error>(())
301    /// ```
302    #[inline]
303    #[must_use]
304    pub fn contains(&self, node: &str) -> bool {
305        self.iter().any(|n| n.as_str() == node)
306    }
307
308    /// Gets a receiver node by index.
309    ///
310    /// Returns `None` if:
311    /// - The index is out of bounds
312    /// - The receiver is `Broadcast` or `None`
313    ///
314    /// # Arguments
315    ///
316    /// * `index` - The zero-based index of the receiver node
317    ///
318    /// # Examples
319    ///
320    /// ```rust,no_run
321    /// use dbc_rs::Dbc;
322    ///
323    /// let dbc = Dbc::parse(r#"VERSION "1.0"
324    ///
325    /// BU_: ECM TCM BCM
326    ///
327    /// BO_ 256 Engine : 8 ECM
328    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
329    /// "#)?;
330    ///
331    /// let message = dbc.messages().at(0).unwrap();
332    /// let signal = message.signals().at(0).unwrap();
333    ///
334    /// assert_eq!(signal.receivers().at(0), Some("TCM"));
335    /// assert_eq!(signal.receivers().at(1), Some("BCM"));
336    /// assert_eq!(signal.receivers().at(2), None);
337    /// # Ok::<(), dbc_rs::Error>(())
338    /// ```
339    #[inline]
340    #[must_use]
341    pub fn at(&self, index: usize) -> Option<&str> {
342        match self {
343            Receivers::Nodes(nodes) => nodes.get(index).map(|s| s.as_str()),
344            Receivers::Broadcast | Receivers::None => None,
345        }
346    }
347}
348
349struct ReceiversIter {
350    nodes: Option<Vec<String<{ MAX_NAME_SIZE }>, { MAX_RECEIVER_NODES }>>,
351    pos: usize,
352}
353
354impl Iterator for ReceiversIter {
355    type Item = String<{ MAX_NAME_SIZE }>;
356    fn next(&mut self) -> Option<Self::Item> {
357        if let Some(nodes) = &self.nodes {
358            if self.pos < nodes.len() {
359                let result: String<{ MAX_NAME_SIZE }> = nodes[self.pos].clone();
360                self.pos += 1;
361                Some(result)
362            } else {
363                None
364            }
365        } else {
366            None
367        }
368    }
369}
370
371#[cfg(test)]
372mod tests {
373    use super::*;
374    use crate::Parser;
375
376    #[test]
377    fn test_parse_receivers_broadcast() {
378        let input = "*";
379        let mut parser = Parser::new(input.as_bytes()).unwrap();
380        let result = Receivers::parse(&mut parser).unwrap();
381        assert_eq!(result, Receivers::Broadcast);
382    }
383
384    #[test]
385    fn test_parse_receivers_none_empty() {
386        // Parser::new returns error for empty input, so use a single space instead
387        // Empty receivers should be handled by Receivers::parse when called from Signal::parse
388        // For this test, we'll test with whitespace-only input
389        let input = " ";
390        let mut parser = Parser::new(input.as_bytes()).unwrap();
391        let result = Receivers::parse(&mut parser).unwrap();
392        assert_eq!(result, Receivers::None);
393    }
394
395    #[test]
396    fn test_parse_receivers_single_node() {
397        let input = "TCM";
398        let mut parser = Parser::new(input.as_bytes()).unwrap();
399        let result = Receivers::parse(&mut parser).unwrap();
400        match &result {
401            Receivers::Nodes(nodes) => {
402                assert_eq!(nodes.len(), 1);
403                let node_count = result.len();
404                assert_eq!(node_count, 1);
405                let first_node = result.iter().next().unwrap();
406                assert_eq!(first_node.as_str(), "TCM");
407            }
408            _ => panic!("Expected Nodes variant"),
409        }
410    }
411
412    #[test]
413    fn test_parse_receivers_multiple_nodes() {
414        let input = "TCM BCM ECM";
415        let mut parser = Parser::new(input.as_bytes()).unwrap();
416        let result = Receivers::parse(&mut parser).unwrap();
417        {
418            let node_count = result.len();
419            assert_eq!(node_count, 3);
420            let mut iter = result.iter();
421            assert_eq!(iter.next().unwrap().as_str(), "TCM");
422            assert_eq!(iter.next().unwrap().as_str(), "BCM");
423            assert_eq!(iter.next().unwrap().as_str(), "ECM");
424            assert!(iter.next().is_none());
425        }
426    }
427
428    #[test]
429    fn test_parse_receivers_whitespace_only() {
430        let input = "   ";
431        let mut parser = Parser::new(input.as_bytes()).unwrap();
432        let result = Receivers::parse(&mut parser).unwrap();
433        assert_eq!(result, Receivers::None);
434    }
435
436    #[test]
437    fn test_parse_receivers_with_extra_whitespace() {
438        let input = "  TCM   BCM  ";
439        let mut parser = Parser::new(input.as_bytes()).unwrap();
440        let result = Receivers::parse(&mut parser).unwrap();
441        let node_count = result.len();
442        assert_eq!(node_count, 2);
443        let mut iter = result.iter();
444        let node1 = iter.next().unwrap();
445        assert_eq!(node1.as_str(), "TCM");
446        let node2 = iter.next().unwrap();
447        assert_eq!(node2.as_str(), "BCM");
448        assert!(iter.next().is_none());
449    }
450
451    // Tests that require std (for format! macro)
452    #[cfg(feature = "std")]
453    mod tests_std {
454        use super::*;
455
456        #[test]
457        fn test_parse_receivers_too_many() {
458            // Create a string with 65 receiver nodes (exceeds limit of 64)
459            // Use std::vec::Vec since we need more than 64 bytes
460            let mut receivers_bytes = std::vec::Vec::new();
461            for i in 0..65 {
462                if i > 0 {
463                    receivers_bytes.push(b' ');
464                }
465                let node_str = format!("Node{i}");
466                receivers_bytes.extend_from_slice(node_str.as_bytes());
467            }
468            let mut parser = Parser::new(&receivers_bytes).unwrap();
469            let result = Receivers::parse(&mut parser);
470            assert!(result.is_err());
471            match result.unwrap_err() {
472                Error::Receivers(msg) => {
473                    assert_eq!(msg, lang::SIGNAL_RECEIVERS_TOO_MANY);
474                }
475                _ => panic!("Expected Error::Receivers"),
476            }
477        }
478
479        #[test]
480        fn test_parse_receivers_at_limit() {
481            // Create a string with exactly 64 receiver nodes (at the limit)
482            // Use std::vec::Vec since we need more than 64 bytes
483            let mut receivers_bytes = std::vec::Vec::new();
484            for i in 0..MAX_RECEIVER_NODES {
485                if i > 0 {
486                    receivers_bytes.push(b' ');
487                }
488                let node_str = format!("Node{i}");
489                receivers_bytes.extend_from_slice(node_str.as_bytes());
490            }
491            let mut parser = Parser::new(&receivers_bytes).unwrap();
492            let result = Receivers::parse(&mut parser).unwrap();
493            let node_count = result.len();
494            assert_eq!(node_count, 64);
495        }
496    }
497}