dbc_rs/receivers/
receivers.rs

1use crate::{Parser, error::ParseError, error::ParseResult, error::messages};
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::Version(messages::SIGNAL_RECEIVERS_TOO_MANY));
123                    }
124                    nodes[count] = Some(node);
125                    count += 1;
126                }
127                Err(ParseError::UnexpectedEof) => break,
128                Err(_) => {
129                    // Failed to parse - if position didn't change, we're at newline or invalid char
130                    if parser.pos() == pos_before {
131                        break;
132                    }
133                    // Position changed but parsing failed - invalid character, also break
134                    break;
135                }
136            }
137        }
138
139        if count == 0 {
140            Ok(Self::new_none())
141        } else {
142            // Collect node names into a slice for new_nodes
143            let mut node_refs: [&'b str; 64] = [""; 64];
144            for i in 0..count {
145                if let Some(node) = nodes[i] {
146                    node_refs[i] = node;
147                }
148            }
149            // Validate before construction
150            const MAX_RECEIVER_NODES: usize = 64;
151            if count > MAX_RECEIVER_NODES {
152                return Err(ParseError::Version(messages::SIGNAL_RECEIVERS_TOO_MANY));
153            }
154            // Construct directly (validation already done)
155            Ok(Self::new_nodes(&node_refs[..count]))
156        }
157    }
158
159    /// Returns an iterator over the receiver node names.
160    ///
161    /// For `Receivers::Broadcast` and `Receivers::None`, the iterator will be empty.
162    /// For `Receivers::Nodes`, it iterates over the specific node names.
163    ///
164    /// # Examples
165    ///
166    /// ```rust,no_run
167    /// use dbc_rs::Dbc;
168    ///
169    /// let dbc = Dbc::parse(r#"VERSION "1.0"
170    ///
171    /// BU_: ECM TCM BCM
172    ///
173    /// BO_ 256 Engine : 8 ECM
174    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
175    /// "#)?;
176    ///
177    /// let message = dbc.messages().at(0).unwrap();
178    /// let signal = message.signals().at(0).unwrap();
179    ///
180    /// // Iterate over receiver nodes
181    /// let mut iter = signal.receivers().iter();
182    /// assert_eq!(iter.next(), Some("TCM"));
183    /// assert_eq!(iter.next(), Some("BCM"));
184    /// assert_eq!(iter.next(), None);
185    /// # Ok::<(), dbc_rs::Error>(())
186    /// ```
187    ///
188    /// # Broadcast and None
189    ///
190    /// ```rust,no_run
191    /// use dbc_rs::Dbc;
192    ///
193    /// let dbc = Dbc::parse(r#"VERSION "1.0"
194    ///
195    /// BU_: ECM
196    ///
197    /// BO_ 256 Engine : 8 ECM
198    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
199    /// "#)?;
200    ///
201    /// let message = dbc.messages().at(0).unwrap();
202    /// let signal = message.signals().at(0).unwrap();
203    ///
204    /// // Broadcast receivers return empty iterator
205    /// assert_eq!(signal.receivers().iter().count(), 0);
206    /// # Ok::<(), dbc_rs::Error>(())
207    /// ```
208    #[inline]
209    #[must_use = "iterator is lazy and does nothing unless consumed"]
210    pub fn iter(&self) -> impl Iterator<Item = &'a str> + '_ {
211        struct NodeIter<'a> {
212            arr: [Option<&'a str>; 64],
213            count: usize,
214            pos: usize,
215        }
216        impl<'a> Iterator for NodeIter<'a> {
217            type Item = &'a str;
218            fn next(&mut self) -> Option<Self::Item> {
219                while self.pos < self.count {
220                    let result = self.arr[self.pos];
221                    self.pos += 1;
222                    if let Some(node) = result {
223                        return Some(node);
224                    }
225                }
226                None
227            }
228        }
229
230        match self {
231            Receivers::Nodes(arr, count) => NodeIter {
232                arr: *arr,
233                count: *count,
234                pos: 0,
235            },
236            _ => NodeIter {
237                arr: [None; 64],
238                count: 0,
239                pos: 0,
240            },
241        }
242    }
243
244    /// Returns the number of receiver nodes.
245    ///
246    /// - For `Receivers::Nodes`: Returns the count of specific receiver nodes
247    /// - For `Receivers::Broadcast` and `Receivers::None`: Returns `0`
248    ///
249    /// # Examples
250    ///
251    /// ```rust,no_run
252    /// use dbc_rs::Dbc;
253    ///
254    /// let dbc = Dbc::parse(r#"VERSION "1.0"
255    ///
256    /// BU_: ECM TCM BCM
257    ///
258    /// BO_ 256 Engine : 8 ECM
259    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
260    /// "#)?;
261    ///
262    /// let message = dbc.messages().at(0).unwrap();
263    /// let signal = message.signals().at(0).unwrap();
264    /// assert_eq!(signal.receivers().len(), 2);
265    /// # Ok::<(), dbc_rs::Error>(())
266    /// ```
267    #[inline]
268    #[must_use]
269    pub fn len(&self) -> usize {
270        match self {
271            Receivers::Nodes(_, count) => *count,
272            Receivers::Broadcast | Receivers::None => 0,
273        }
274    }
275
276    /// Returns `true` if there are no specific receiver nodes.
277    ///
278    /// This returns `true` for both `Receivers::Broadcast` and `Receivers::None`,
279    /// as neither has specific node names.
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
289    ///
290    /// BO_ 256 Engine : 8 ECM
291    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
292    /// "#)?;
293    ///
294    /// let message = dbc.messages().at(0).unwrap();
295    /// let signal = message.signals().at(0).unwrap();
296    /// assert!(signal.receivers().is_empty());
297    /// # Ok::<(), dbc_rs::Error>(())
298    /// ```
299    #[inline]
300    #[must_use]
301    pub fn is_empty(&self) -> bool {
302        self.len() == 0
303    }
304
305    /// Checks if a node name is in the receivers list.
306    ///
307    /// For `Receivers::Broadcast` and `Receivers::None`, this always returns `false`.
308    /// For `Receivers::Nodes`, it checks if the node name is in the list.
309    ///
310    /// # Arguments
311    ///
312    /// * `node` - The node name to check
313    ///
314    /// # Examples
315    ///
316    /// ```rust,no_run
317    /// use dbc_rs::Dbc;
318    ///
319    /// let dbc = Dbc::parse(r#"VERSION "1.0"
320    ///
321    /// BU_: ECM TCM BCM
322    ///
323    /// BO_ 256 Engine : 8 ECM
324    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
325    /// "#)?;
326    ///
327    /// let message = dbc.messages().at(0).unwrap();
328    /// let signal = message.signals().at(0).unwrap();
329    ///
330    /// assert!(signal.receivers().contains("TCM"));
331    /// assert!(signal.receivers().contains("BCM"));
332    /// assert!(!signal.receivers().contains("ECM"));
333    /// # Ok::<(), dbc_rs::Error>(())
334    /// ```
335    #[inline]
336    #[must_use]
337    pub fn contains(&self, node: &str) -> bool {
338        self.iter().any(|n| n == node)
339    }
340
341    /// Gets a receiver node by index.
342    ///
343    /// Returns `None` if:
344    /// - The index is out of bounds
345    /// - The receiver is `Broadcast` or `None`
346    ///
347    /// # Arguments
348    ///
349    /// * `index` - The zero-based index of the receiver node
350    ///
351    /// # Examples
352    ///
353    /// ```rust,no_run
354    /// use dbc_rs::Dbc;
355    ///
356    /// let dbc = Dbc::parse(r#"VERSION "1.0"
357    ///
358    /// BU_: ECM TCM BCM
359    ///
360    /// BO_ 256 Engine : 8 ECM
361    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "°C" TCM BCM
362    /// "#)?;
363    ///
364    /// let message = dbc.messages().at(0).unwrap();
365    /// let signal = message.signals().at(0).unwrap();
366    ///
367    /// assert_eq!(signal.receivers().at(0), Some("TCM"));
368    /// assert_eq!(signal.receivers().at(1), Some("BCM"));
369    /// assert_eq!(signal.receivers().at(2), None);
370    /// # Ok::<(), dbc_rs::Error>(())
371    /// ```
372    #[inline]
373    #[must_use]
374    pub fn at(&self, index: usize) -> Option<&'a str> {
375        match self {
376            Receivers::Nodes(arr, count) => {
377                if index >= *count {
378                    return None;
379                }
380                arr[index]
381            }
382            Receivers::Broadcast | Receivers::None => None,
383        }
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::*;
390    use crate::Parser;
391    #[cfg(any(feature = "alloc", feature = "kernel"))]
392    use crate::error::{ParseError, lang};
393    #[cfg(any(feature = "alloc", feature = "kernel"))]
394    use alloc::format;
395
396    #[test]
397    fn test_parse_receivers_broadcast() {
398        let input = "*";
399        let mut parser = Parser::new(input.as_bytes()).unwrap();
400        let result = Receivers::parse(&mut parser).unwrap();
401        assert_eq!(result, Receivers::Broadcast);
402    }
403
404    #[test]
405    fn test_parse_receivers_none_empty() {
406        // Parser::new returns error for empty input, so use a single space instead
407        // Empty receivers should be handled by Receivers::parse when called from Signal::parse
408        // For this test, we'll test with whitespace-only input
409        let input = " ";
410        let mut parser = Parser::new(input.as_bytes()).unwrap();
411        let result = Receivers::parse(&mut parser).unwrap();
412        assert_eq!(result, Receivers::None);
413    }
414
415    #[test]
416    fn test_parse_receivers_single_node() {
417        let input = "TCM";
418        let mut parser = Parser::new(input.as_bytes()).unwrap();
419        let result = Receivers::parse(&mut parser).unwrap();
420        match result {
421            Receivers::Nodes(_, count) => {
422                assert_eq!(count, 1);
423                let node_count = result.len();
424                assert_eq!(node_count, 1);
425                let first_node = result.iter().next().unwrap();
426                assert_eq!(first_node, "TCM");
427            }
428            _ => panic!("Expected Nodes variant"),
429        }
430    }
431
432    #[test]
433    fn test_parse_receivers_multiple_nodes() {
434        let input = "TCM BCM ECM";
435        let mut parser = Parser::new(input.as_bytes()).unwrap();
436        let result = Receivers::parse(&mut parser).unwrap();
437        {
438            let node_count = result.len();
439            assert_eq!(node_count, 3);
440            let mut iter = result.iter();
441            assert_eq!(iter.next().unwrap(), "TCM");
442            assert_eq!(iter.next().unwrap(), "BCM");
443            assert_eq!(iter.next().unwrap(), "ECM");
444            assert!(iter.next().is_none());
445        }
446    }
447
448    #[test]
449    fn test_parse_receivers_whitespace_only() {
450        let input = "   ";
451        let mut parser = Parser::new(input.as_bytes()).unwrap();
452        let result = Receivers::parse(&mut parser).unwrap();
453        assert_eq!(result, Receivers::None);
454    }
455
456    #[test]
457    fn test_parse_receivers_with_extra_whitespace() {
458        let input = "  TCM   BCM  ";
459        let mut parser = Parser::new(input.as_bytes()).unwrap();
460        let result = Receivers::parse(&mut parser).unwrap();
461        let node_count = result.len();
462        assert_eq!(node_count, 2);
463        let mut iter = result.iter();
464        assert_eq!(iter.next(), Some("TCM"));
465        assert_eq!(iter.next(), Some("BCM"));
466        assert_eq!(iter.next(), None);
467    }
468
469    #[test]
470    #[cfg(any(feature = "alloc", feature = "kernel"))]
471    fn test_parse_receivers_too_many() {
472        // Create a string with 65 receiver nodes (exceeds limit of 64)
473        // Use a simple approach: create byte array directly
474        use crate::compat::Vec;
475        use alloc::format;
476        let mut receivers_bytes = Vec::new();
477        for i in 0..65 {
478            if i > 0 {
479                receivers_bytes.push(b' ');
480            }
481            let node_str = format!("Node{i}");
482            receivers_bytes.extend_from_slice(node_str.as_bytes());
483        }
484        let mut parser = Parser::new(&receivers_bytes).unwrap();
485        let result = Receivers::parse(&mut parser);
486        assert!(result.is_err());
487        match result.unwrap_err() {
488            ParseError::Version(msg) => {
489                assert!(msg.contains(lang::SIGNAL_RECEIVERS_TOO_MANY));
490            }
491            _ => panic!("Expected ParseError"),
492        }
493    }
494
495    #[test]
496    #[cfg(any(feature = "alloc", feature = "kernel"))]
497    fn test_parse_receivers_at_limit() {
498        // Create a string with exactly 64 receiver nodes (at the limit)
499        // Use a simple approach: create byte array directly
500        use crate::compat::Vec;
501        use alloc::format;
502        let mut receivers_bytes = Vec::new();
503        for i in 0..64 {
504            if i > 0 {
505                receivers_bytes.push(b' ');
506            }
507            let node_str = format!("Node{i}");
508            receivers_bytes.extend_from_slice(node_str.as_bytes());
509        }
510        let mut parser = Parser::new(&receivers_bytes).unwrap();
511        let result = Receivers::parse(&mut parser).unwrap();
512        let node_count = result.len();
513        assert_eq!(node_count, 64);
514    }
515}