dbc_rs/message/
message.rs

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