dbc_rs/message/
message.rs

1use crate::{
2    ByteOrder, Parser, Signal,
3    error::{ParseError, ParseResult},
4};
5
6use super::Signals;
7
8/// Represents a CAN message in a DBC file.
9///
10/// A `Message` contains:
11/// - A unique ID (CAN identifier)
12/// - A name
13/// - A DLC (Data Length Code) specifying the message size in bytes
14/// - A sender node (ECU that transmits this message)
15/// - A collection of signals
16///
17/// # Examples
18///
19/// ```rust,no_run
20/// use dbc_rs::Dbc;
21///
22/// let dbc_content = r#"VERSION "1.0"
23///
24/// BU_: ECM
25///
26/// BO_ 256 EngineData : 8 ECM
27///  SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
28/// "#;
29///
30/// let dbc = Dbc::parse(dbc_content)?;
31/// let message = dbc.messages().at(0).unwrap();
32/// println!("Message: {} (ID: {})", message.name(), message.id());
33/// # Ok::<(), dbc_rs::Error>(())
34/// ```
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct Message<'a> {
37    id: u32,
38    name: &'a str,
39    dlc: u8,
40    sender: &'a str,
41    signals: Signals<'a>,
42}
43
44impl<'a> Message<'a> {
45    #[allow(clippy::similar_names)] // physical_lsb and physical_msb are intentionally similar
46    fn calculate_bit_range(start_bit: u16, length: u16, byte_order: ByteOrder) -> (u16, u16) {
47        let start = start_bit;
48        let len = length;
49
50        match byte_order {
51            ByteOrder::LittleEndian => {
52                // Little-endian: start_bit is LSB, signal extends forward
53                // Range: [start_bit, start_bit + length - 1]
54                (start, start + len - 1)
55            }
56            ByteOrder::BigEndian => {
57                // Big-endian: start_bit is MSB in big-endian numbering, signal extends backward
58                // The big-endian bit numbering follows Vector convention:
59                // be_bits = [7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, ...]
60                // This means: BE bit 0 -> physical bit 7, BE bit 7 -> physical bit 0
61                //            BE bit 8 -> physical bit 15, BE bit 15 -> physical bit 8
62                // To find the physical bit range:
63                // 1. Find the index of start_bit in the be_bits sequence
64                // 2. MSB (physical) = be_bits[idx]
65                // 3. LSB (physical) = be_bits[idx + length - 1]
66                // We can calculate this directly:
67                // For BE bit N: byte_num = N / 8, bit_in_byte = N % 8
68                // Physical bit = byte_num * 8 + (7 - bit_in_byte)
69                let byte_num = start / 8;
70                let bit_in_byte = start % 8;
71                let physical_msb = byte_num * 8 + (7 - bit_in_byte);
72
73                // Calculate LSB: move forward (length - 1) positions in the BE sequence
74                // BE bit (start + length - 1) maps to physical bit
75                let lsb_be_bit = start + len - 1;
76                let lsb_byte_num = lsb_be_bit / 8;
77                let lsb_bit_in_byte = lsb_be_bit % 8;
78                let physical_lsb = lsb_byte_num * 8 + (7 - lsb_bit_in_byte);
79
80                // Ensure lsb <= msb (they should be in that order for big-endian)
81                if physical_lsb <= physical_msb {
82                    (physical_lsb, physical_msb)
83                } else {
84                    (physical_msb, physical_lsb)
85                }
86            }
87        }
88    }
89
90    #[allow(clippy::similar_names)] // Overlap detection uses intentionally similar variable names (sig1_lsb/sig1_msb, sig2_lsb/sig2_msb)
91    pub(crate) fn validate_internal(
92        id: u32,
93        name: &str,
94        dlc: u8,
95        sender: &str,
96        signals: &[Option<Signal<'a>>],
97        signal_count: usize,
98        options: crate::ParseOptions,
99    ) -> ParseResult<()> {
100        // Validate CAN ID range
101        // CAN specification allows:
102        // - Standard 11-bit IDs: 0x000 to 0x7FF (0-2047)
103        // - Extended 29-bit IDs: 0x00000000 to 0x1FFFFFFF (0-536870911)
104        // Note: Extended IDs can technically be 0-536870911, but DBC files typically
105        // use the convention where IDs 0-2047 are treated as standard and 2048+ as extended.
106        // We only validate the maximum range here; the distinction between standard/extended
107        // is determined by the ID value in practice.
108        const MAX_EXTENDED_ID: u32 = 0x1FFF_FFFF; // 536870911
109
110        // Check signal count limit per message (DoS protection)
111        const MAX_SIGNALS_PER_MESSAGE: usize = crate::Signals::max_capacity();
112        if signal_count > MAX_SIGNALS_PER_MESSAGE {
113            return Err(ParseError::Version(
114                crate::error::lang::MESSAGE_TOO_MANY_SIGNALS,
115            ));
116        }
117
118        if name.trim().is_empty() {
119            return Err(ParseError::Version(crate::error::lang::MESSAGE_NAME_EMPTY));
120        }
121
122        if sender.trim().is_empty() {
123            return Err(ParseError::Version(
124                crate::error::lang::MESSAGE_SENDER_EMPTY,
125            ));
126        }
127
128        // Validate DLC (Data Length Code): must be between 1 and 64 bytes
129        // - Classic CAN Standard (CAN 2.0A): DLC <= 8 bytes (64 bits) maximum payload
130        // - Classic CAN Extended (CAN 2.0B): DLC <= 8 bytes (64 bits) maximum payload
131        // - CAN FD (Flexible Data Rate, ISO/Bosch): DLC <= 64 bytes (512 bits) maximum payload
132        if dlc == 0 {
133            #[cfg(feature = "alloc")]
134            {
135                use crate::error::{messages, version_error_from_string};
136                let msg = messages::message_dlc_too_small(name, id, dlc);
137                return Err(version_error_from_string(msg));
138            }
139            #[cfg(not(feature = "alloc"))]
140            {
141                return Err(ParseError::Version(
142                    crate::error::lang::MESSAGE_DLC_TOO_SMALL,
143                ));
144            }
145        }
146        if dlc > 64 {
147            #[cfg(feature = "alloc")]
148            {
149                use crate::error::{messages, version_error_from_string};
150                let msg = messages::message_dlc_too_large(name, id, dlc);
151                return Err(version_error_from_string(msg));
152            }
153            #[cfg(not(feature = "alloc"))]
154            {
155                return Err(ParseError::Version(
156                    crate::error::lang::MESSAGE_DLC_TOO_LARGE,
157                ));
158            }
159        }
160
161        // Validate that ID is within valid CAN ID range
162        // Extended CAN IDs can be 0x00000000 to 0x1FFFFFFF (0 to 536870911)
163        // IDs exceeding 0x1FFFFFFF are invalid
164        if id > MAX_EXTENDED_ID {
165            #[cfg(feature = "alloc")]
166            {
167                use crate::error::{messages, version_error_from_string};
168                let msg = messages::message_id_out_of_range(id);
169                return Err(version_error_from_string(msg));
170            }
171            #[cfg(not(feature = "alloc"))]
172            {
173                return Err(ParseError::Version(
174                    crate::error::lang::MESSAGE_ID_OUT_OF_RANGE,
175                ));
176            }
177        }
178
179        // Validate that all signals fit within the message boundary
180        // Each signal must fit within: DLC * 8 bits
181        // - Classic CAN (2.0A/2.0B): DLC * 8 <= 64 bits (8 bytes)
182        // - CAN FD: DLC * 8 <= 512 bits (64 bytes)
183        // This ensures no signal extends beyond the message payload capacity
184        let max_bits = u16::from(dlc) * 8;
185        for signal in signals.iter().take(signal_count).filter_map(|opt| opt.as_ref()) {
186            // Calculate the actual bit range for this signal (accounting for byte order)
187            let (lsb, msb) =
188                Self::calculate_bit_range(signal.start_bit(), signal.length(), signal.byte_order());
189            // Check if the signal extends beyond the message boundary
190            // The signal's highest bit position must be less than max_bits
191            let signal_max_bit = lsb.max(msb);
192            if signal_max_bit >= max_bits {
193                // Only fail if strict boundary checking is enabled
194                if options.strict_boundary_check {
195                    #[cfg(feature = "alloc")]
196                    {
197                        use crate::error::messages;
198                        let msg = messages::signal_extends_beyond_message(
199                            signal.name(),
200                            signal.start_bit(),
201                            signal.length(),
202                            signal_max_bit,
203                            max_bits,
204                            dlc,
205                        );
206                        use crate::error::version_error_from_string;
207                        return Err(version_error_from_string(msg));
208                    }
209                    #[cfg(not(feature = "alloc"))]
210                    {
211                        return Err(ParseError::Version(
212                            crate::error::lang::SIGNAL_LENGTH_TOO_LARGE,
213                        ));
214                    }
215                }
216                // In lenient mode, we allow signals that extend beyond boundaries
217            }
218        }
219
220        // Validate signal overlap detection
221        // Check if any two signals overlap in the same message
222        // Must account for byte order: little-endian signals extend forward,
223        // big-endian signals extend backward from start_bit
224        // We iterate over pairs without collecting to avoid alloc
225        let signals_slice = &signals[..signal_count];
226        for (i, sig1_opt) in signals_slice.iter().enumerate() {
227            let sig1 = match sig1_opt {
228                Some(s) => s,
229                None => continue, // Should not happen, but be safe
230            };
231            let (sig1_lsb, sig1_msb) =
232                Self::calculate_bit_range(sig1.start_bit(), sig1.length(), sig1.byte_order());
233
234            for sig2_opt in signals_slice.iter().skip(i + 1) {
235                let sig2 = match sig2_opt {
236                    Some(s) => s,
237                    None => continue, // Should not happen, but be safe
238                };
239                let (sig2_lsb, sig2_msb) =
240                    Self::calculate_bit_range(sig2.start_bit(), sig2.length(), sig2.byte_order());
241
242                // Check if ranges overlap
243                // Two ranges [lsb1, msb1] and [lsb2, msb2] overlap if:
244                // lsb1 <= msb2 && lsb2 <= msb1
245                if sig1_lsb <= sig2_msb && sig2_lsb <= sig1_msb {
246                    #[cfg(feature = "alloc")]
247                    {
248                        use crate::error::{messages, version_error_from_string};
249                        let msg = messages::signal_overlap(sig1.name(), sig2.name(), name);
250                        return Err(version_error_from_string(msg));
251                    }
252                    #[cfg(not(feature = "alloc"))]
253                    {
254                        return Err(ParseError::Version(crate::error::lang::SIGNAL_OVERLAP));
255                    }
256                }
257            }
258        }
259
260        Ok(())
261    }
262
263    #[allow(dead_code)] // Only used by builders (std-only)
264    pub(crate) fn new(
265        id: u32,
266        name: &'a str,
267        dlc: u8,
268        sender: &'a str,
269        signals: &'a [Signal<'a>],
270    ) -> Self {
271        // Validation should have been done prior (by builder or parse)
272        Self {
273            id,
274            name,
275            dlc,
276            sender,
277            signals: Signals::from_signals_slice(signals),
278        }
279    }
280
281    fn new_from_options(
282        id: u32,
283        name: &'a str,
284        dlc: u8,
285        sender: &'a str,
286        signals: &[Option<Signal<'a>>],
287        signal_count: usize,
288    ) -> Self {
289        // Validation should have been done prior (by builder or parse)
290        Self {
291            id,
292            name,
293            dlc,
294            sender,
295            signals: Signals::from_options_slice(signals, signal_count),
296        }
297    }
298
299    pub(crate) fn parse<'b: 'a>(
300        parser: &mut Parser<'b>,
301        signals: &[Option<Signal<'a>>],
302        signal_count: usize,
303        options: crate::ParseOptions,
304    ) -> ParseResult<Self> {
305        // Expect "BO_" keyword (should already be consumed by find_next_keyword, but handle both cases)
306        if parser.expect(crate::BO_.as_bytes()).is_err() {
307            // Already past "BO_" from find_next_keyword, continue
308        }
309
310        // Skip whitespace
311        let _ = parser.skip_whitespace();
312
313        // Parse message ID
314        let id = parser
315            .parse_u32()
316            .map_err(|_| ParseError::Version(crate::error::lang::MESSAGE_INVALID_ID))?;
317
318        // Skip whitespace
319        parser
320            .skip_whitespace()
321            .map_err(|_| ParseError::Expected("Expected whitespace"))?;
322
323        // Parse message name (identifier)
324        let name = parser
325            .parse_identifier()
326            .map_err(|_| ParseError::Version(crate::error::lang::MESSAGE_NAME_EMPTY))?;
327
328        // Skip whitespace (optional before colon)
329        let _ = parser.skip_whitespace();
330
331        // Expect colon
332        parser.expect(b":").map_err(|_| ParseError::Expected("Expected colon"))?;
333
334        // Skip whitespace after colon
335        let _ = parser.skip_whitespace();
336
337        // Parse DLC
338        let dlc = parser
339            .parse_u8()
340            .map_err(|_| ParseError::Version(crate::error::lang::MESSAGE_INVALID_DLC))?;
341
342        // Skip whitespace
343        parser
344            .skip_whitespace()
345            .map_err(|_| ParseError::Expected("Expected whitespace"))?;
346
347        // Parse sender (identifier, until end of line or whitespace)
348        let sender = parser
349            .parse_identifier()
350            .map_err(|_| ParseError::Version(crate::error::lang::MESSAGE_SENDER_EMPTY))?;
351
352        // Check for extra content after sender (invalid format)
353        parser.skip_newlines_and_spaces();
354        if !parser.is_empty() {
355            return Err(ParseError::Expected(
356                "Unexpected content after message sender",
357            ));
358        }
359
360        // Validate before construction
361        Self::validate_internal(
362            id,
363            name,
364            dlc,
365            sender,
366            &signals[..signal_count],
367            signal_count,
368            options,
369        )?;
370        // Construct directly (validation already done)
371        Ok(Self::new_from_options(
372            id,
373            name,
374            dlc,
375            sender,
376            signals,
377            signal_count,
378        ))
379    }
380
381    /// Returns the CAN message ID.
382    ///
383    /// # Examples
384    ///
385    /// ```rust,no_run
386    /// use dbc_rs::Dbc;
387    ///
388    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
389    /// let message = dbc.messages().at(0).unwrap();
390    /// assert_eq!(message.id(), 256);
391    /// # Ok::<(), dbc_rs::Error>(())
392    /// ```
393    #[inline]
394    #[must_use]
395    pub fn id(&self) -> u32 {
396        self.id
397    }
398
399    /// Returns the message name.
400    ///
401    /// # Examples
402    ///
403    /// ```rust,no_run
404    /// use dbc_rs::Dbc;
405    ///
406    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
407    /// let message = dbc.messages().at(0).unwrap();
408    /// assert_eq!(message.name(), "EngineData");
409    /// # Ok::<(), dbc_rs::Error>(())
410    /// ```
411    #[inline]
412    #[must_use]
413    pub fn name(&self) -> &'a str {
414        self.name
415    }
416
417    /// Returns the Data Length Code (DLC) in bytes.
418    ///
419    /// DLC specifies the size of the message payload. For classic CAN, this is 1-8 bytes.
420    /// For CAN FD, this can be up to 64 bytes.
421    ///
422    /// # Examples
423    ///
424    /// ```rust,no_run
425    /// use dbc_rs::Dbc;
426    ///
427    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
428    /// let message = dbc.messages().at(0).unwrap();
429    /// assert_eq!(message.dlc(), 8);
430    /// # Ok::<(), dbc_rs::Error>(())
431    /// ```
432    #[inline]
433    #[must_use]
434    pub fn dlc(&self) -> u8 {
435        self.dlc
436    }
437
438    #[inline]
439    #[must_use]
440    pub fn sender(&self) -> &'a str {
441        self.sender
442    }
443
444    /// Get a reference to the signals collection
445    #[inline]
446    #[must_use]
447    pub fn signals(&self) -> &Signals<'a> {
448        &self.signals
449    }
450
451    #[cfg(feature = "alloc")]
452    #[must_use]
453    pub fn to_dbc_string(&self) -> alloc::string::String {
454        use alloc::format;
455        format!(
456            "BO_ {} {} : {} {}",
457            self.id(),
458            self.name(),
459            self.dlc(),
460            self.sender()
461        )
462    }
463
464    #[cfg(feature = "alloc")]
465    #[must_use]
466    pub fn to_dbc_string_with_signals(&self) -> alloc::string::String {
467        use alloc::string::String;
468        let mut result = String::with_capacity(200 + (self.signals.len() * 100));
469        result.push_str(&self.to_dbc_string());
470        result.push('\n');
471
472        for signal in self.signals().iter() {
473            result.push_str(&signal.to_dbc_string());
474            result.push('\n');
475        }
476
477        result
478    }
479}
480
481#[cfg(feature = "alloc")]
482impl<'a> core::fmt::Display for Message<'a> {
483    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
484        write!(f, "{}", self.to_dbc_string_with_signals())
485    }
486}
487
488#[cfg(test)]
489mod tests {
490    #![allow(clippy::float_cmp)]
491    use super::*;
492    use crate::{Parser, error::ParseError};
493    #[cfg(any(feature = "alloc", feature = "kernel"))]
494    use alloc::{format, vec};
495    // Note: Builder tests have been moved to message_builder.rs
496    // This module only tests Message parsing and direct API usage
497
498    // Note: All test_message_new_* tests have been removed - they belong in message_builder.rs
499    // This module only tests Message parsing and direct API usage
500
501    #[test]
502    fn test_message_parse_valid() {
503        let data = b"BO_ 256 EngineData : 8 ECM";
504        let mut parser = Parser::new(data).unwrap();
505        const MAX_CAP: usize = crate::Signals::max_capacity();
506        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
507        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
508        assert_eq!(message.id(), 256);
509        assert_eq!(message.name(), "EngineData");
510        assert_eq!(message.dlc(), 8);
511        assert_eq!(message.sender(), "ECM");
512        assert_eq!(message.signals().len(), 0);
513    }
514
515    #[test]
516    fn test_message_parse_invalid_id() {
517        let data = b"BO_ invalid EngineData : 8 ECM";
518        let mut parser = Parser::new(data).unwrap();
519        const MAX_CAP: usize = crate::Signals::max_capacity();
520        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
521        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
522        assert!(result.is_err());
523        match result.unwrap_err() {
524            ParseError::Version(_) => {
525                // Expected
526            }
527            _ => panic!("Expected ParseError::Version"),
528        }
529    }
530
531    #[test]
532    fn test_message_parse_empty_name() {
533        let data = b"BO_ 256  : 8 ECM";
534        let mut parser = Parser::new(data).unwrap();
535        const MAX_CAP: usize = crate::Signals::max_capacity();
536        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
537        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
538        assert!(result.is_err());
539        match result.unwrap_err() {
540            ParseError::Version(_) => {
541                // Expected
542            }
543            _ => panic!("Expected ParseError::Version"),
544        }
545    }
546
547    #[test]
548    fn test_message_parse_invalid_dlc() {
549        let data = b"BO_ 256 EngineData : invalid ECM";
550        let mut parser = Parser::new(data).unwrap();
551        const MAX_CAP: usize = crate::Signals::max_capacity();
552        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
553        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
554        assert!(result.is_err());
555        match result.unwrap_err() {
556            ParseError::Version(_) => {
557                // Expected
558            }
559            _ => panic!("Expected ParseError::Version"),
560        }
561    }
562
563    #[test]
564    fn test_message_parse_empty_sender() {
565        let data = b"BO_ 256 EngineData : 8 ";
566        let mut parser = Parser::new(data).unwrap();
567        const MAX_CAP: usize = crate::Signals::max_capacity();
568        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
569        let result = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new());
570        assert!(result.is_err());
571        match result.unwrap_err() {
572            ParseError::Version(_) => {
573                // Expected
574            }
575            _ => panic!("Expected ParseError::Version"),
576        }
577    }
578
579    #[test]
580    fn test_message_parse_with_signals() {
581        let data = b"BO_ 256 EngineData : 8 ECM";
582        let mut parser = Parser::new(data).unwrap();
583
584        // Create test signals
585        let signal1 = Signal::parse(
586            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
587        )
588        .unwrap();
589        let signal2 = Signal::parse(
590            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
591        )
592        .unwrap();
593
594        const MAX_CAP: usize = crate::Signals::max_capacity();
595        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
596        signals[0] = Some(signal1);
597        signals[1] = Some(signal2);
598
599        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
600        assert_eq!(message.id(), 256);
601        assert_eq!(message.name(), "EngineData");
602        assert_eq!(message.dlc(), 8);
603        assert_eq!(message.sender(), "ECM");
604        assert_eq!(message.signals().len(), 2);
605    }
606
607    #[test]
608    fn test_message_signals_iterator() {
609        let data = b"BO_ 256 EngineData : 8 ECM";
610        let mut parser = Parser::new(data).unwrap();
611
612        // Create test signals
613        let signal1 = Signal::parse(
614            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
615        )
616        .unwrap();
617        let signal2 = Signal::parse(
618            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
619        )
620        .unwrap();
621
622        const MAX_CAP: usize = crate::Signals::max_capacity();
623        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
624        signals[0] = Some(signal1);
625        signals[1] = Some(signal2);
626
627        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
628        let mut signals_iter = message.signals().iter();
629        assert_eq!(signals_iter.next().unwrap().name(), "RPM");
630        assert_eq!(signals_iter.next().unwrap().name(), "Temp");
631        assert!(signals_iter.next().is_none());
632    }
633
634    #[test]
635    fn test_message_signal_count() {
636        let data = b"BO_ 256 EngineData : 8 ECM";
637        let mut parser = Parser::new(data).unwrap();
638
639        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
640            [const { None }; crate::Signals::max_capacity()];
641        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
642        assert_eq!(message.signals().len(), 0);
643
644        // Create a new parser for the second parse since the first one consumed the input
645        let data2 = b"BO_ 256 EngineData : 8 ECM";
646        let mut parser2 = Parser::new(data2).unwrap();
647        let signal1 = Signal::parse(
648            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
649        )
650        .unwrap();
651        const MAX_CAP: usize = crate::Signals::max_capacity();
652        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
653        signals[0] = Some(signal1);
654        let message =
655            Message::parse(&mut parser2, &signals, 1, crate::ParseOptions::new()).unwrap();
656        assert_eq!(message.signals().len(), 1);
657    }
658
659    #[test]
660    fn test_message_signal_at() {
661        let data = b"BO_ 256 EngineData : 8 ECM";
662        let mut parser = Parser::new(data).unwrap();
663
664        let signal1 = Signal::parse(
665            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
666        )
667        .unwrap();
668        let signal2 = Signal::parse(
669            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
670        )
671        .unwrap();
672
673        const MAX_CAP: usize = crate::Signals::max_capacity();
674        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
675        signals[0] = Some(signal1);
676        signals[1] = Some(signal2);
677
678        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
679        assert_eq!(message.signals().at(0).unwrap().name(), "RPM");
680        assert_eq!(message.signals().at(1).unwrap().name(), "Temp");
681        assert!(message.signals().at(2).is_none());
682    }
683
684    #[test]
685    fn test_message_find_signal() {
686        let data = b"BO_ 256 EngineData : 8 ECM";
687        let mut parser = Parser::new(data).unwrap();
688
689        let signal1 = Signal::parse(
690            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
691        )
692        .unwrap();
693        let signal2 = Signal::parse(
694            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
695        )
696        .unwrap();
697
698        const MAX_CAP: usize = crate::Signals::max_capacity();
699        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
700        signals[0] = Some(signal1);
701        signals[1] = Some(signal2);
702
703        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
704        assert_eq!(message.signals().find("RPM").unwrap().name(), "RPM");
705        assert_eq!(message.signals().find("Temp").unwrap().name(), "Temp");
706        assert!(message.signals().find("Nonexistent").is_none());
707    }
708
709    #[test]
710    #[cfg(feature = "alloc")]
711    fn test_message_to_dbc_string() {
712        let data = b"BO_ 256 EngineData : 8 ECM";
713        let mut parser = Parser::new(data).unwrap();
714        const MAX_CAP: usize = crate::Signals::max_capacity();
715        let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
716        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
717        let dbc_string = message.to_dbc_string();
718        assert_eq!(dbc_string, "BO_ 256 EngineData : 8 ECM");
719    }
720
721    #[test]
722    #[cfg(feature = "alloc")]
723    fn test_message_to_dbc_string_with_signals() {
724        let data = b"BO_ 256 EngineData : 8 ECM";
725        let mut parser = Parser::new(data).unwrap();
726
727        let signal1 = Signal::parse(
728            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
729        )
730        .unwrap();
731        let signal2 = Signal::parse(
732            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
733        )
734        .unwrap();
735
736        const MAX_CAP: usize = crate::Signals::max_capacity();
737        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
738        signals[0] = Some(signal1);
739        signals[1] = Some(signal2);
740
741        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
742        let dbc_string = message.to_dbc_string_with_signals();
743        assert!(dbc_string.contains("BO_ 256 EngineData : 8 ECM"));
744        assert!(dbc_string.contains("SG_ RPM"));
745        assert!(dbc_string.contains("SG_ Temp"));
746    }
747
748    #[test]
749    #[cfg(any(feature = "alloc", feature = "kernel"))]
750    fn test_message_can_2_0a_dlc_limits() {
751        use alloc::format;
752        // CAN 2.0A: DLC can be 1-8 bytes (8-64 bits)
753        // Test valid DLC values
754        for dlc in 1..=8 {
755            let data = format!("BO_ 256 EngineData : {} ECM", dlc);
756            let mut parser = Parser::new(data.as_bytes()).unwrap();
757            const MAX_CAP: usize = crate::Signals::max_capacity();
758            let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
759            let message =
760                Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
761            assert_eq!(message.dlc(), dlc);
762        }
763    }
764
765    #[test]
766    #[cfg(any(feature = "alloc", feature = "kernel"))]
767    fn test_message_can_2_0b_dlc_limits() {
768        use alloc::format;
769        // CAN 2.0B: DLC can be 1-8 bytes (8-64 bits)
770        // Test valid DLC values
771        for dlc in 1..=8 {
772            let data = format!("BO_ 256 EngineData : {} ECM", dlc);
773            let mut parser = Parser::new(data.as_bytes()).unwrap();
774            const MAX_CAP: usize = crate::Signals::max_capacity();
775            let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
776            let message =
777                Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
778            assert_eq!(message.dlc(), dlc);
779        }
780    }
781
782    #[test]
783    #[cfg(any(feature = "alloc", feature = "kernel"))]
784    fn test_message_can_fd_dlc_limits() {
785        use alloc::format;
786        // CAN FD: DLC can be 1-64 bytes (8-512 bits)
787        // Test valid DLC values up to 64
788        for dlc in [1, 8, 12, 16, 20, 24, 32, 48, 64] {
789            let data = format!("BO_ 256 EngineData : {} ECM", dlc);
790            let mut parser = Parser::new(data.as_bytes()).unwrap();
791            const MAX_CAP: usize = crate::Signals::max_capacity();
792            let signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
793            let message =
794                Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
795            assert_eq!(message.dlc(), dlc);
796        }
797    }
798
799    #[test]
800    fn test_message_multiple_signals_boundary_validation() {
801        // Test that signals at message boundaries are validated correctly
802        let data = b"BO_ 256 EngineData : 8 ECM";
803        let mut parser = Parser::new(data).unwrap();
804
805        // Create signals that exactly fill the message (8 bytes = 64 bits)
806        // Signal 1: bits 0-15 (16 bits)
807        let signal1 =
808            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|16@0+ (1,0) [0|65535] \"\"").unwrap())
809                .unwrap();
810        // Signal 2: bits 16-31 (16 bits)
811        let signal2 = Signal::parse(
812            &mut Parser::new(b"SG_ Signal2 : 16|16@0+ (1,0) [0|65535] \"\"").unwrap(),
813        )
814        .unwrap();
815        // Signal 3: bits 32-47 (16 bits)
816        let signal3 = Signal::parse(
817            &mut Parser::new(b"SG_ Signal3 : 32|16@0+ (1,0) [0|65535] \"\"").unwrap(),
818        )
819        .unwrap();
820        // Signal 4: bits 48-63 (16 bits) - exactly at boundary
821        let signal4 = Signal::parse(
822            &mut Parser::new(b"SG_ Signal4 : 48|16@0+ (1,0) [0|65535] \"\"").unwrap(),
823        )
824        .unwrap();
825
826        const MAX_CAP: usize = crate::Signals::max_capacity();
827        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
828        signals[0] = Some(signal1);
829        signals[1] = Some(signal2);
830        signals[2] = Some(signal3);
831        signals[3] = Some(signal4);
832
833        let message = Message::parse(&mut parser, &signals, 4, crate::ParseOptions::new()).unwrap();
834        assert_eq!(message.signals().len(), 4);
835    }
836
837    #[test]
838    fn test_message_big_endian_bit_range_calculation() {
839        // Test big-endian bit range calculation
840        // BE bit 0 -> physical bit 7
841        // BE bit 7 -> physical bit 0
842        // BE bit 8 -> physical bit 15
843        // BE bit 15 -> physical bit 8
844        let data = b"BO_ 256 EngineData : 8 ECM";
845        let mut parser = Parser::new(data).unwrap();
846
847        // Signal starting at BE bit 0, length 8 -> should map to physical bits 0-7
848        let signal =
849            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@1+ (1,0) [0|255] \"\"").unwrap())
850                .unwrap();
851
852        const MAX_CAP: usize = crate::Signals::max_capacity();
853        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
854        signals[0] = Some(signal);
855
856        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
857        // The signal should be valid and fit within the message
858        assert_eq!(message.signals().len(), 1);
859    }
860
861    #[test]
862    fn test_message_little_endian_bit_range_calculation() {
863        // Test little-endian bit range calculation
864        // LE bit N -> physical bit N (straightforward mapping)
865        let data = b"BO_ 256 EngineData : 8 ECM";
866        let mut parser = Parser::new(data).unwrap();
867
868        // Signal starting at LE bit 0, length 8 -> should map to physical bits 0-7
869        let signal =
870            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
871                .unwrap();
872
873        const MAX_CAP: usize = crate::Signals::max_capacity();
874        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
875        signals[0] = Some(signal);
876
877        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
878        // The signal should be valid and fit within the message
879        assert_eq!(message.signals().len(), 1);
880    }
881
882    // Note: Big-endian signal overlap and boundary tests have been moved to message_builder.rs
883
884    #[test]
885    fn test_parse_with_lenient_boundary_check() {
886        // Test that lenient mode allows signals that extend beyond message boundaries
887        let data = b"BO_ 256 Test : 8 ECM";
888        let mut parser = Parser::new(data).unwrap();
889
890        // Signal that extends beyond 8-byte boundary (start_bit 63, length 8 = bits 63-70, exceeds 64 bits)
891        let signal =
892            Signal::parse(&mut Parser::new(b"SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] \"\"").unwrap())
893                .unwrap();
894
895        const MAX_CAP: usize = crate::Signals::max_capacity();
896        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
897        signals[0] = Some(signal);
898
899        // Strict mode should fail
900        let result = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new());
901        assert!(result.is_err());
902
903        // Lenient mode should succeed
904        let mut parser2 = Parser::new(data).unwrap();
905        let message =
906            Message::parse(&mut parser2, &signals, 1, crate::ParseOptions::lenient()).unwrap();
907        assert_eq!(message.signals().len(), 1);
908        assert_eq!(message.signals().at(0).unwrap().name(), "CHECKSUM");
909    }
910
911    #[test]
912    #[cfg(feature = "alloc")]
913    fn test_message_to_dbc_string_empty_signals() {
914        let data = b"BO_ 256 EngineData : 8 ECM";
915        let mut parser = Parser::new(data).unwrap();
916        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
917            [const { None }; crate::Signals::max_capacity()];
918        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
919
920        let dbc_string = message.to_dbc_string();
921        assert_eq!(dbc_string, "BO_ 256 EngineData : 8 ECM");
922
923        let dbc_string_with_signals = message.to_dbc_string_with_signals();
924        assert_eq!(dbc_string_with_signals, "BO_ 256 EngineData : 8 ECM\n");
925    }
926
927    #[test]
928    #[cfg(feature = "alloc")]
929    fn test_message_to_dbc_string_special_characters() {
930        let data = b"BO_ 1234 Test_Message_With_Underscores : 4 Sender_Node";
931        let mut parser = Parser::new(data).unwrap();
932        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
933            [const { None }; crate::Signals::max_capacity()];
934        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
935
936        let dbc_string = message.to_dbc_string();
937        assert_eq!(
938            dbc_string,
939            "BO_ 1234 Test_Message_With_Underscores : 4 Sender_Node"
940        );
941    }
942
943    #[test]
944    #[cfg(feature = "alloc")]
945    fn test_message_to_dbc_string_extended_id() {
946        // Use a valid extended ID (max is 0x1FFF_FFFF = 536870911)
947        let data = b"BO_ 536870911 ExtendedID : 8 ECM";
948        let mut parser = Parser::new(data).unwrap();
949        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
950            [const { None }; crate::Signals::max_capacity()];
951        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
952
953        let dbc_string = message.to_dbc_string();
954        assert_eq!(dbc_string, "BO_ 536870911 ExtendedID : 8 ECM");
955    }
956
957    #[test]
958    #[cfg(feature = "alloc")]
959    fn test_message_to_dbc_string_dlc_edge_cases() {
960        // Test DLC = 1
961        let data = b"BO_ 256 MinDLC : 1 ECM";
962        let mut parser = Parser::new(data).unwrap();
963        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
964            [const { None }; crate::Signals::max_capacity()];
965        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
966        assert_eq!(message.to_dbc_string(), "BO_ 256 MinDLC : 1 ECM");
967
968        // Test DLC = 64 (CAN FD max)
969        let data2 = b"BO_ 257 MaxDLC : 64 ECM";
970        let mut parser2 = Parser::new(data2).unwrap();
971        let message2 =
972            Message::parse(&mut parser2, &signals, 0, crate::ParseOptions::new()).unwrap();
973        assert_eq!(message2.to_dbc_string(), "BO_ 257 MaxDLC : 64 ECM");
974    }
975
976    #[test]
977    fn test_message_signals_is_empty() {
978        let data = b"BO_ 256 EngineData : 8 ECM";
979        let mut parser = Parser::new(data).unwrap();
980        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
981            [const { None }; crate::Signals::max_capacity()];
982        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
983        assert!(message.signals().is_empty());
984        assert_eq!(message.signals().len(), 0);
985    }
986
987    #[test]
988    fn test_message_signals_at_out_of_bounds() {
989        let data = b"BO_ 256 EngineData : 8 ECM";
990        let mut parser = Parser::new(data).unwrap();
991
992        let signal = Signal::parse(
993            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
994        )
995        .unwrap();
996
997        const MAX_CAP: usize = crate::Signals::max_capacity();
998        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
999        signals[0] = Some(signal);
1000
1001        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
1002
1003        // Valid index
1004        assert!(message.signals().at(0).is_some());
1005        assert_eq!(message.signals().at(0).unwrap().name(), "RPM");
1006
1007        // Out of bounds
1008        assert!(message.signals().at(1).is_none());
1009        assert!(message.signals().at(100).is_none());
1010        assert!(message.signals().at(usize::MAX).is_none());
1011    }
1012
1013    #[test]
1014    fn test_message_signals_find_case_sensitive() {
1015        let data = b"BO_ 256 EngineData : 8 ECM";
1016        let mut parser = Parser::new(data).unwrap();
1017
1018        let signal1 = Signal::parse(
1019            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
1020        )
1021        .unwrap();
1022        let signal2 = Signal::parse(
1023            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
1024        )
1025        .unwrap();
1026
1027        const MAX_CAP: usize = crate::Signals::max_capacity();
1028        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
1029        signals[0] = Some(signal1);
1030        signals[1] = Some(signal2);
1031
1032        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
1033
1034        // Exact match
1035        assert!(message.signals().find("RPM").is_some());
1036        assert_eq!(message.signals().find("RPM").unwrap().name(), "RPM");
1037
1038        // Case sensitive - should not find
1039        assert!(message.signals().find("rpm").is_none());
1040        assert!(message.signals().find("Rpm").is_none());
1041
1042        // Find second signal
1043        assert!(message.signals().find("Temp").is_some());
1044        assert_eq!(message.signals().find("Temp").unwrap().name(), "Temp");
1045
1046        // Not found
1047        assert!(message.signals().find("Nonexistent").is_none());
1048        assert!(message.signals().find("").is_none());
1049    }
1050
1051    #[test]
1052    fn test_message_signals_find_empty_collection() {
1053        let data = b"BO_ 256 EngineData : 8 ECM";
1054        let mut parser = Parser::new(data).unwrap();
1055        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
1056            [const { None }; crate::Signals::max_capacity()];
1057        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
1058
1059        assert!(message.signals().find("RPM").is_none());
1060        assert!(message.signals().find("").is_none());
1061    }
1062
1063    #[test]
1064    #[cfg(feature = "alloc")]
1065    fn test_message_display_trait() {
1066        use alloc::format;
1067        let data = b"BO_ 256 EngineData : 8 ECM";
1068        let mut parser = Parser::new(data).unwrap();
1069
1070        // Parse signal from DBC string instead of using builder
1071        let signal = Signal::parse(
1072            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *").unwrap(),
1073        )
1074        .unwrap();
1075
1076        const MAX_CAP: usize = crate::Signals::max_capacity();
1077        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
1078        signals[0] = Some(signal);
1079
1080        let message = Message::parse(&mut parser, &signals, 1, crate::ParseOptions::new()).unwrap();
1081
1082        let display_str = format!("{}", message);
1083        assert!(display_str.contains("BO_ 256 EngineData : 8 ECM"));
1084        assert!(display_str.contains("SG_ RPM"));
1085    }
1086
1087    #[test]
1088    #[cfg(feature = "alloc")]
1089    fn test_message_to_dbc_string_with_signals_multiple() {
1090        let data = b"BO_ 256 EngineData : 8 ECM";
1091        let mut parser = Parser::new(data).unwrap();
1092
1093        // Parse signals from DBC strings instead of using builders
1094        let signal1 = Signal::parse(
1095            &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *").unwrap(),
1096        )
1097        .unwrap();
1098
1099        let signal2 = Signal::parse(
1100            &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\" *").unwrap(),
1101        )
1102        .unwrap();
1103
1104        const MAX_CAP: usize = crate::Signals::max_capacity();
1105        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
1106        signals[0] = Some(signal1);
1107        signals[1] = Some(signal2);
1108
1109        let message = Message::parse(&mut parser, &signals, 2, crate::ParseOptions::new()).unwrap();
1110
1111        let dbc_string = message.to_dbc_string_with_signals();
1112        assert!(dbc_string.contains("BO_ 256 EngineData : 8 ECM"));
1113        assert!(dbc_string.contains("SG_ RPM"));
1114        assert!(dbc_string.contains("SG_ Temp"));
1115        // Should have newlines between signals
1116        #[cfg(any(feature = "alloc", feature = "kernel"))]
1117        use crate::compat::Vec;
1118        let lines: Vec<&str> = dbc_string.lines().collect();
1119        assert!(lines.len() >= 3); // Message line + at least 2 signal lines
1120    }
1121
1122    #[test]
1123    fn test_message_getters_edge_cases() {
1124        // Test with minimum values
1125        let data = b"BO_ 0 A : 1 B";
1126        let mut parser = Parser::new(data).unwrap();
1127        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
1128            [const { None }; crate::Signals::max_capacity()];
1129        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
1130
1131        assert_eq!(message.id(), 0);
1132        assert_eq!(message.name(), "A");
1133        assert_eq!(message.dlc(), 1);
1134        assert_eq!(message.sender(), "B");
1135    }
1136
1137    #[test]
1138    fn test_message_signals_iterator_empty() {
1139        let data = b"BO_ 256 EngineData : 8 ECM";
1140        let mut parser = Parser::new(data).unwrap();
1141        let signals: [Option<Signal>; crate::Signals::max_capacity()] =
1142            [const { None }; crate::Signals::max_capacity()];
1143        let message = Message::parse(&mut parser, &signals, 0, crate::ParseOptions::new()).unwrap();
1144
1145        let mut iter = message.signals().iter();
1146        assert!(iter.next().is_none());
1147    }
1148
1149    #[test]
1150    fn test_message_signals_iterator_multiple() {
1151        let data = b"BO_ 256 EngineData : 8 ECM";
1152        let mut parser = Parser::new(data).unwrap();
1153
1154        let signal1 =
1155            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
1156                .unwrap();
1157        let signal2 =
1158            Signal::parse(&mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap())
1159                .unwrap();
1160        let signal3 =
1161            Signal::parse(&mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap())
1162                .unwrap();
1163
1164        const MAX_CAP: usize = crate::Signals::max_capacity();
1165        let mut signals: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
1166        signals[0] = Some(signal1);
1167        signals[1] = Some(signal2);
1168        signals[2] = Some(signal3);
1169
1170        let message = Message::parse(&mut parser, &signals, 3, crate::ParseOptions::new()).unwrap();
1171
1172        let mut iter = message.signals().iter();
1173        assert_eq!(iter.next().unwrap().name(), "Signal1");
1174        assert_eq!(iter.next().unwrap().name(), "Signal2");
1175        assert_eq!(iter.next().unwrap().name(), "Signal3");
1176        assert!(iter.next().is_none());
1177
1178        // Test that iterator can be used multiple times
1179        #[cfg(any(feature = "alloc", feature = "kernel"))]
1180        {
1181            use crate::compat::Vec;
1182            use alloc::vec;
1183            let names: Vec<&str> = message.signals().iter().map(|s| s.name()).collect();
1184            // Convert kernel Vec to alloc Vec for comparison
1185            #[cfg(feature = "kernel")]
1186            let names: alloc::vec::Vec<&str> = names.into();
1187            assert_eq!(names, vec!["Signal1", "Signal2", "Signal3"]);
1188        }
1189    }
1190}