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}