dbc_rs/receivers/
receivers.rs

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