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}