dbc_rs/dbc/
dbc.rs

1#[cfg(any(feature = "alloc", feature = "kernel"))]
2use crate::dbc::ValueDescriptionsList;
3#[cfg(any(feature = "alloc", feature = "kernel"))]
4use crate::value_descriptions::ValueDescriptions;
5#[cfg(feature = "alloc")]
6use crate::{Error, Result};
7use crate::{
8    Message, MessageList, Nodes, ParseOptions, Parser, Signal, Signals, Version,
9    error::{ParseError, ParseResult},
10};
11
12#[cfg(feature = "alloc")]
13use alloc::string::String;
14
15/// Represents a complete DBC (CAN database) file.
16///
17/// A `Dbc` contains:
18/// - An optional version string
19/// - A list of nodes (ECUs)
20/// - A collection of messages with their signals
21///
22/// # Examples
23///
24/// ```rust,no_run
25/// use dbc_rs::Dbc;
26///
27/// let dbc_content = r#"VERSION "1.0"
28///
29/// BU_: ECM TCM
30///
31/// BO_ 256 EngineData : 8 ECM
32///  SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" TCM
33/// "#;
34///
35/// let dbc = Dbc::parse(dbc_content)?;
36/// println!("Parsed {} messages", dbc.messages().len());
37/// # Ok::<(), dbc_rs::Error>(())
38/// ```
39#[derive(Debug)]
40pub struct Dbc<'a> {
41    version: Option<Version<'a>>,
42    nodes: Nodes<'a>,
43    messages: MessageList<'a>,
44    #[cfg(any(feature = "alloc", feature = "kernel"))]
45    value_descriptions: crate::dbc::ValueDescriptionsList<'a>,
46}
47
48impl<'a> Dbc<'a> {
49    // Validate function for alloc/kernel features (with value_descriptions)
50    #[cfg(any(feature = "alloc", feature = "kernel"))]
51    pub(crate) fn validate(
52        nodes: &Nodes<'_>,
53        messages: &[Option<Message<'_>>],
54        message_count: usize,
55        value_descriptions: Option<&crate::dbc::ValueDescriptionsList<'_>>,
56    ) -> ParseResult<()> {
57        Self::validate_common(nodes, messages, message_count)?;
58
59        // Validate value descriptions if provided
60        if let Some(value_descriptions) = value_descriptions {
61            let messages_slice = &messages[..message_count];
62            // Validate that all value descriptions reference existing messages and signals
63            for ((message_id_opt, signal_name), _) in value_descriptions.iter() {
64                // Check if message exists (for message-specific value descriptions)
65                if let Some(message_id) = message_id_opt {
66                    let message_exists = messages_slice
67                        .iter()
68                        .any(|msg_opt| msg_opt.as_ref().is_some_and(|msg| msg.id() == message_id));
69                    if !message_exists {
70                        return Err(ParseError::Message(
71                            crate::error::lang::VALUE_DESCRIPTION_MESSAGE_NOT_FOUND,
72                        ));
73                    }
74
75                    // Check if signal exists in the message
76                    let signal_exists = messages_slice.iter().any(|msg_opt| {
77                        msg_opt.as_ref().is_some_and(|msg| {
78                            msg.id() == message_id && msg.signals().find(signal_name).is_some()
79                        })
80                    });
81                    if !signal_exists {
82                        return Err(ParseError::Message(
83                            crate::error::lang::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND,
84                        ));
85                    }
86                } else {
87                    // For global value descriptions (message_id is None), check if signal exists in any message
88                    let signal_exists = messages_slice.iter().any(|msg_opt| {
89                        msg_opt
90                            .as_ref()
91                            .is_some_and(|msg| msg.signals().find(signal_name).is_some())
92                    });
93                    if !signal_exists {
94                        return Err(ParseError::Message(
95                            crate::error::lang::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND,
96                        ));
97                    }
98                }
99            }
100        }
101
102        Ok(())
103    }
104
105    // Validate function for no_std mode (without value_descriptions)
106    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
107    pub(crate) fn validate(
108        nodes: &Nodes<'_>,
109        messages: &[Option<Message<'_>>],
110        message_count: usize,
111    ) -> ParseResult<()> {
112        Self::validate_common(nodes, messages, message_count)
113    }
114
115    // Common validation logic shared by both versions
116    fn validate_common(
117        nodes: &Nodes<'_>,
118        messages: &[Option<Message<'_>>],
119        message_count: usize,
120    ) -> ParseResult<()> {
121        // Check for duplicate message IDs
122        let messages_slice = &messages[..message_count];
123        for (i, msg1_opt) in messages_slice.iter().enumerate() {
124            let msg1 = match msg1_opt {
125                Some(m) => m,
126                None => continue, // Should not happen, but be safe
127            };
128            for msg2_opt in messages_slice.iter().skip(i + 1) {
129                let msg2 = match msg2_opt {
130                    Some(m) => m,
131                    None => continue, // Should not happen, but be safe
132                };
133                if msg1.id() == msg2.id() {
134                    return Err(ParseError::Message(
135                        crate::error::lang::DUPLICATE_MESSAGE_ID,
136                    ));
137                }
138            }
139        }
140
141        // Validate that all message senders are in the nodes list
142        // Skip validation if nodes list is empty (empty nodes allowed per DBC spec)
143        if !nodes.is_empty() {
144            for msg_opt in messages_slice {
145                let msg = match msg_opt {
146                    Some(m) => m,
147                    None => continue, // Should not happen, but be safe
148                };
149                if !nodes.contains(msg.sender()) {
150                    return Err(ParseError::Message(crate::error::lang::SENDER_NOT_IN_NODES));
151                }
152            }
153        }
154
155        Ok(())
156    }
157
158    #[cfg(any(feature = "alloc", feature = "kernel"))]
159    pub(crate) fn new(
160        version: Option<Version<'a>>,
161        nodes: Nodes<'a>,
162        messages: &'a [Message<'a>],
163        value_descriptions: crate::dbc::value_descriptions_list::ValueDescriptionsList<'a>,
164    ) -> Self {
165        // Validation should have been done prior (by builder)
166        Self {
167            version,
168            nodes,
169            messages: MessageList::from_messages_slice(messages),
170            value_descriptions,
171        }
172    }
173
174    #[cfg(any(feature = "alloc", feature = "kernel"))]
175    fn new_from_options_with_value_descriptions(
176        version: Option<Version<'a>>,
177        nodes: Nodes<'a>,
178        messages: &[Option<Message<'a>>],
179        message_count: usize,
180        value_descriptions: crate::dbc::value_descriptions_list::ValueDescriptionsList<'a>,
181    ) -> Self {
182        // Validation should have been done prior (by parse)
183        Self {
184            version,
185            nodes,
186            messages: MessageList::from_options_slice(messages, message_count),
187            value_descriptions,
188        }
189    }
190
191    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
192    fn new_from_options(
193        version: Option<Version<'a>>,
194        nodes: Nodes<'a>,
195        messages: &[Option<Message<'a>>],
196        message_count: usize,
197    ) -> Self {
198        // Validation should have been done prior (by parse)
199        Self {
200            version,
201            nodes,
202            messages: MessageList::from_options_slice(messages, message_count),
203        }
204    }
205
206    /// Get the version of the DBC file
207    ///
208    /// # Examples
209    ///
210    /// ```rust,no_run
211    /// use dbc_rs::Dbc;
212    ///
213    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
214    /// if let Some(version) = dbc.version() {
215    ///     // Version is available
216    ///     let _ = version.as_str();
217    /// }
218    /// # Ok::<(), dbc_rs::Error>(())
219    /// ```
220    #[inline]
221    #[must_use]
222    pub fn version(&self) -> Option<&Version<'a>> {
223        self.version.as_ref()
224    }
225
226    /// Get a reference to the nodes collection
227    ///
228    /// # Examples
229    ///
230    /// ```rust,no_run
231    /// use dbc_rs::Dbc;
232    ///
233    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM TCM\n\nBO_ 256 Engine : 8 ECM")?;
234    /// let nodes = dbc.nodes();
235    /// assert_eq!(nodes.len(), 2);
236    /// // Iterate over nodes
237    /// let mut iter = nodes.iter();
238    /// assert_eq!(iter.next(), Some("ECM"));
239    /// assert_eq!(iter.next(), Some("TCM"));
240    /// assert_eq!(iter.next(), None);
241    /// # Ok::<(), dbc_rs::Error>(())
242    /// ```
243    #[inline]
244    #[must_use]
245    pub fn nodes(&self) -> &Nodes<'a> {
246        &self.nodes
247    }
248
249    /// Get a reference to the messages collection
250    ///
251    /// # Examples
252    ///
253    /// ```rust,no_run
254    /// use dbc_rs::Dbc;
255    ///
256    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
257    /// let messages = dbc.messages();
258    /// assert_eq!(messages.len(), 1);
259    /// let message = messages.at(0).unwrap();
260    /// assert_eq!(message.name(), "Engine");
261    /// assert_eq!(message.id(), 256);
262    /// # Ok::<(), dbc_rs::Error>(())
263    /// ```
264    #[inline]
265    #[must_use]
266    pub fn messages(&self) -> &MessageList<'a> {
267        &self.messages
268    }
269
270    /// Get value descriptions for a specific signal
271    ///
272    /// Value descriptions map numeric signal values to human-readable text.
273    /// Returns `None` if the signal has no value descriptions.
274    ///
275    /// **Global Value Descriptions**: According to the Vector DBC specification,
276    /// a message_id of `-1` (0xFFFFFFFF) in a `VAL_` statement means the value
277    /// descriptions apply to all signals with that name in ANY message. This
278    /// method will first check for a message-specific entry, then fall back to
279    /// a global entry if one exists.
280    ///
281    /// # Examples
282    ///
283    /// ```rust,no_run
284    /// # use dbc_rs::Dbc;
285    /// # let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 100 Engine : 8 ECM\n SG_ Gear : 0|8@1+ (1,0) [0|5] "" *\n\nVAL_ 100 Gear 0 "Park" 1 "Reverse" ;"#)?;
286    /// if let Some(value_descriptions) = dbc.value_descriptions_for_signal(100, "Gear") {
287    ///     if let Some(desc) = value_descriptions.get(0) {
288    ///         println!("Value 0 means: {}", desc);
289    ///     }
290    /// }
291    /// # Ok::<(), dbc_rs::Error>(())
292    /// ```
293    /// Get a reference to the value descriptions list
294    ///
295    /// # Examples
296    ///
297    /// ```rust,no_run
298    /// use dbc_rs::Dbc;
299    ///
300    /// let dbc = Dbc::parse(r#"VERSION "1.0"
301    ///
302    /// BU_: ECM
303    ///
304    /// BO_ 100 Engine : 8 ECM
305    ///  SG_ Gear : 0|8@1+ (1,0) [0|5] "" *
306    ///
307    /// VAL_ 100 Gear 0 "Park" 1 "Drive" ;"#)?;
308    /// let value_descriptions_list = dbc.value_descriptions();
309    /// assert_eq!(value_descriptions_list.len(), 1);
310    /// # Ok::<(), dbc_rs::Error>(())
311    /// ```
312    #[cfg(any(feature = "alloc", feature = "kernel"))]
313    #[inline]
314    #[must_use]
315    pub fn value_descriptions(&self) -> &ValueDescriptionsList<'a> {
316        &self.value_descriptions
317    }
318
319    #[cfg(any(feature = "alloc", feature = "kernel"))]
320    #[must_use]
321    pub fn value_descriptions_for_signal(
322        &self,
323        message_id: u32,
324        signal_name: &str,
325    ) -> Option<&ValueDescriptions<'a>> {
326        self.value_descriptions.for_signal(message_id, signal_name)
327    }
328
329    /// Parse a DBC file from a string slice
330    ///
331    /// # Examples
332    ///
333    /// ```rust,no_run
334    /// use dbc_rs::Dbc;
335    ///
336    /// let dbc_content = r#"VERSION "1.0"
337    ///
338    /// BU_: ECM
339    ///
340    /// BO_ 256 EngineData : 8 ECM
341    ///  SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm""#;
342    ///
343    /// let dbc = Dbc::parse(dbc_content)?;
344    /// assert_eq!(dbc.messages().len(), 1);
345    /// # Ok::<(), dbc_rs::Error>(())
346    /// ```
347    pub fn parse(data: &'a str) -> ParseResult<Self> {
348        Self::parse_with_options(data, ParseOptions::default())
349    }
350
351    /// Parses a DBC file from a string with custom parsing options.
352    ///
353    /// # Arguments
354    ///
355    /// * `data` - The DBC file content as a string
356    /// * `options` - Parsing options to control validation behavior
357    ///
358    /// # Examples
359    ///
360    /// ```rust,no_run
361    /// use dbc_rs::{Dbc, ParseOptions};
362    ///
363    /// let dbc_content = r#"VERSION "1.0"
364    ///
365    /// BU_: ECM
366    ///
367    /// BO_ 256 Test : 8 ECM
368    ///  SG_ Signal1 : 0|8@1+ (1,0) [0|255] ""
369    /// "#;
370    ///
371    /// // Use lenient mode to allow signals that extend beyond message boundaries
372    /// let options = ParseOptions::lenient();
373    /// let dbc = Dbc::parse_with_options(dbc_content, options)?;
374    /// # Ok::<(), dbc_rs::Error>(())
375    /// ```
376    pub fn parse_with_options(data: &'a str, options: ParseOptions) -> ParseResult<Self> {
377        // FIRST PASS: Count messages (two-pass parsing to allocate correct sizes)
378        let mut parser1 = Parser::new(data.as_bytes())?;
379        let _ = MessageList::count_messages_and_signals(&mut parser1)?;
380
381        // SECOND PASS: Parse into messages array
382        let mut parser2 = Parser::new(data.as_bytes())?;
383
384        // Allocate messages buffer - MessageList will handle the size internally
385        // We use a temporary buffer that MessageList can work with (no alloc in no_std)
386        // MessageList handles capacity internally, we just need a buffer
387        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
388        let mut messages_buffer = MessageList::new_parse_buffer();
389
390        #[cfg(any(feature = "alloc", feature = "kernel"))]
391        let mut messages_buffer: alloc::vec::Vec<Option<Message<'a>>> = {
392            use crate::compat::vec_with_capacity;
393            vec_with_capacity(MessageList::max_capacity())
394        };
395
396        let mut message_count_actual = 0;
397
398        // Parse version, nodes, and messages
399        use crate::{
400            BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SIG_GROUP_,
401            SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
402        };
403
404        let mut version: Option<Version<'a>> = None;
405        let mut nodes: Option<Nodes<'a>> = None;
406
407        // Store value descriptions during parsing: (message_id, signal_name, value, description)
408        #[cfg(any(feature = "alloc", feature = "kernel"))]
409        type ValueDescriptionsBufferEntry<'a> =
410            (Option<u32>, &'a str, alloc::vec::Vec<(u64, &'a str)>);
411        #[cfg(any(feature = "alloc", feature = "kernel"))]
412        let mut value_descriptions_buffer: alloc::vec::Vec<
413            ValueDescriptionsBufferEntry<'a>,
414        > = alloc::vec::Vec::new();
415
416        loop {
417            // Skip comments (lines starting with //)
418            parser2.skip_newlines_and_spaces();
419            if parser2.starts_with(b"//") {
420                parser2.skip_to_end_of_line();
421                continue;
422            }
423
424            let keyword_result = parser2.peek_next_keyword();
425            let keyword = match keyword_result {
426                Ok(kw) => kw,
427                Err(ParseError::UnexpectedEof) => break,
428                Err(ParseError::Expected(_)) => {
429                    if parser2.starts_with(b"//") {
430                        parser2.skip_to_end_of_line();
431                        continue;
432                    }
433                    return Err(keyword_result.unwrap_err());
434                }
435                Err(e) => return Err(e),
436            };
437
438            // Save position after peek_next_keyword (which skips whitespace, so we're at the keyword)
439            let pos_at_keyword = parser2.pos();
440
441            match keyword {
442                NS_ => {
443                    // Consume NS_ keyword
444                    parser2
445                        .expect(crate::NS_.as_bytes())
446                        .map_err(|_| ParseError::Expected("Failed to consume NS_ keyword"))?;
447                    parser2.skip_newlines_and_spaces();
448                    let _ = parser2.expect(b":").ok();
449                    loop {
450                        parser2.skip_newlines_and_spaces();
451                        if parser2.is_empty() {
452                            break;
453                        }
454                        if parser2.starts_with(b" ") || parser2.starts_with(b"\t") {
455                            parser2.skip_to_end_of_line();
456                            continue;
457                        }
458                        if parser2.starts_with(b"//") {
459                            parser2.skip_to_end_of_line();
460                            continue;
461                        }
462                        if parser2.starts_with(BS_.as_bytes())
463                            || parser2.starts_with(BU_.as_bytes())
464                            || parser2.starts_with(BO_.as_bytes())
465                            || parser2.starts_with(SG_.as_bytes())
466                            || parser2.starts_with(VERSION.as_bytes())
467                        {
468                            break;
469                        }
470                        parser2.skip_to_end_of_line();
471                    }
472                    continue;
473                }
474                CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | SIG_GROUP_
475                | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
476                    // Consume keyword then skip to end of line
477                    let _ = parser2.expect(keyword.as_bytes()).ok();
478                    parser2.skip_to_end_of_line();
479                    continue;
480                }
481                VAL_ => {
482                    #[cfg(any(feature = "alloc", feature = "kernel"))]
483                    {
484                        // Consume VAL_ keyword
485                        let _ = parser2.expect(crate::VAL_.as_bytes()).ok();
486                        // Parse VAL_ statement: VAL_ message_id signal_name value1 "desc1" value2 "desc2" ... ;
487                        // Note: message_id of -1 (0xFFFFFFFF) means the value descriptions apply to
488                        // all signals with this name in ANY message (global value descriptions)
489                        parser2.skip_newlines_and_spaces();
490                        let message_id = match parser2.parse_i64() {
491                            Ok(id) => {
492                                // -1 (0xFFFFFFFF) is the magic number for global value descriptions
493                                if id == -1 {
494                                    None
495                                } else if id >= 0 && id <= u32::MAX as i64 {
496                                    Some(id as u32)
497                                } else {
498                                    parser2.skip_to_end_of_line();
499                                    continue;
500                                }
501                            }
502                            Err(_) => {
503                                parser2.skip_to_end_of_line();
504                                continue;
505                            }
506                        };
507                        parser2.skip_newlines_and_spaces();
508                        let signal_name = match parser2.parse_identifier() {
509                            Ok(name) => name,
510                            Err(_) => {
511                                parser2.skip_to_end_of_line();
512                                continue;
513                            }
514                        };
515                        // Parse value-description pairs
516                        let mut entries = alloc::vec::Vec::new();
517                        loop {
518                            parser2.skip_newlines_and_spaces();
519                            // Check for semicolon (end of VAL_ statement)
520                            if parser2.starts_with(b";") {
521                                parser2.expect(b";").ok();
522                                break;
523                            }
524                            // Parse value (as i64 first to handle negative values like -1, then convert to u64)
525                            // Note: -1 (0xFFFFFFFF) is the magic number for global value descriptions in message_id,
526                            // but values in VAL_ can also be negative
527                            let value = match parser2.parse_i64() {
528                                Ok(v) => {
529                                    // Handle -1 specially: convert to 0xFFFFFFFF (u32::MAX) instead of large u64
530                                    if v == -1 { 0xFFFF_FFFFu64 } else { v as u64 }
531                                }
532                                Err(_) => {
533                                    parser2.skip_to_end_of_line();
534                                    break;
535                                }
536                            };
537                            parser2.skip_newlines_and_spaces();
538                            // Parse description string (expect quote, then take until quote)
539                            if parser2.expect(b"\"").is_err() {
540                                parser2.skip_to_end_of_line();
541                                break;
542                            }
543                            let description_bytes = match parser2.take_until_quote(false, 1024) {
544                                Ok(bytes) => bytes,
545                                Err(_) => {
546                                    parser2.skip_to_end_of_line();
547                                    break;
548                                }
549                            };
550                            let description = match core::str::from_utf8(description_bytes) {
551                                Ok(s) => s,
552                                Err(_) => {
553                                    parser2.skip_to_end_of_line();
554                                    break;
555                                }
556                            };
557                            entries.push((value, description));
558                        }
559                        if !entries.is_empty() {
560                            value_descriptions_buffer.push((message_id, signal_name, entries));
561                        }
562                    }
563                    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
564                    {
565                        // In no_std mode, consume VAL_ keyword and skip the rest
566                        let _ = parser2.expect(crate::VAL_.as_bytes()).ok();
567                        parser2.skip_to_end_of_line();
568                    }
569                    continue;
570                }
571                VERSION => {
572                    // Version::parse expects VERSION keyword, don't consume it here
573                    version = Some(Version::parse(&mut parser2)?);
574                    continue;
575                }
576                BU_ => {
577                    // Nodes::parse expects BU_ keyword, create parser from original input including it
578                    parser2.skip_to_end_of_line();
579                    let bu_input = &data.as_bytes()[pos_at_keyword..parser2.pos()];
580                    let mut bu_parser = Parser::new(bu_input)?;
581                    nodes = Some(Nodes::parse(&mut bu_parser)?);
582                    continue;
583                }
584                BO_ => {
585                    // Check limit using Messages (which knows about the capacity)
586                    if message_count_actual >= MessageList::max_capacity() {
587                        return Err(ParseError::Nodes(crate::error::lang::NODES_TOO_MANY));
588                    }
589
590                    // Save parser position (at BO_ keyword, so Message::parse can consume it)
591                    let message_start_pos = pos_at_keyword;
592
593                    // Don't manually parse - just find where the header ends by looking for the colon and sender
594                    // We need to find the end of the header line to separate it from signals
595                    let header_line_end = {
596                        // Skip to end of line to find where header ends
597                        let mut temp_parser = Parser::new(&data.as_bytes()[pos_at_keyword..])?;
598                        // Skip BO_ keyword
599                        temp_parser.expect(crate::BO_.as_bytes()).ok();
600                        temp_parser.skip_whitespace().ok();
601                        temp_parser.parse_u32().ok(); // ID
602                        temp_parser.skip_whitespace().ok();
603                        temp_parser.parse_identifier().ok(); // name
604                        temp_parser.skip_whitespace().ok();
605                        temp_parser.expect(b":").ok(); // colon
606                        temp_parser.skip_whitespace().ok();
607                        temp_parser.parse_u8().ok(); // DLC
608                        temp_parser.skip_whitespace().ok();
609                        temp_parser.parse_identifier().ok(); // sender
610                        pos_at_keyword + temp_parser.pos()
611                    };
612
613                    // Now parse signals from the original parser
614                    parser2.skip_to_end_of_line(); // Skip past header line
615
616                    // Parse signals into fixed array
617                    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
618                    let mut signals_array = Signals::new_parse_buffer();
619
620                    #[cfg(any(feature = "alloc", feature = "kernel"))]
621                    let mut signals_array: alloc::vec::Vec<Option<Signal<'a>>> = {
622                        use crate::compat::vec_with_capacity;
623                        vec_with_capacity(Signals::max_capacity())
624                    };
625
626                    let mut signal_count = 0;
627                    loop {
628                        parser2.skip_newlines_and_spaces();
629                        if parser2.starts_with(crate::SG_.as_bytes()) {
630                            if let Some(next_byte) = parser2.peek_byte_at(3) {
631                                if matches!(next_byte, b' ' | b'\n' | b'\r' | b'\t') {
632                                    if signal_count >= Signals::max_capacity() {
633                                        return Err(ParseError::Receivers(
634                                            crate::error::lang::SIGNAL_RECEIVERS_TOO_MANY,
635                                        ));
636                                    }
637                                    // Signal::parse expects SG_ keyword, which we've already verified with starts_with
638                                    let signal = Signal::parse(&mut parser2)?;
639                                    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
640                                    {
641                                        signals_array[signal_count] = Some(signal);
642                                    }
643                                    #[cfg(any(feature = "alloc", feature = "kernel"))]
644                                    {
645                                        signals_array.push(Some(signal));
646                                    }
647                                    signal_count += 1;
648                                    continue;
649                                }
650                            }
651                        }
652                        break;
653                    }
654
655                    // Restore parser to start of message line and use Message::parse
656                    // Create a new parser from the original input, but only up to the end of the header
657                    // (not including signals, so Message::parse doesn't complain about extra content)
658                    let message_input = &data.as_bytes()[message_start_pos..header_line_end];
659                    let mut message_parser = Parser::new(message_input)?;
660
661                    // Use Message::parse which will parse the header and use our signals
662                    let signals_slice: &[Option<Signal<'a>>] = {
663                        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
664                        {
665                            &signals_array[..signal_count]
666                        }
667                        #[cfg(any(feature = "alloc", feature = "kernel"))]
668                        {
669                            &signals_array[..]
670                        }
671                    };
672                    let message =
673                        Message::parse(&mut message_parser, signals_slice, signal_count, options)?;
674
675                    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
676                    {
677                        messages_buffer[message_count_actual] = Some(message);
678                    }
679                    #[cfg(any(feature = "alloc", feature = "kernel"))]
680                    {
681                        messages_buffer.push(Some(message));
682                    }
683                    message_count_actual += 1;
684                    continue;
685                }
686                SG_ => {
687                    // Orphaned signal (not inside a message) - skip it
688                    parser2.skip_to_end_of_line();
689                    continue;
690                }
691                _ => {
692                    parser2.skip_to_end_of_line();
693                    continue;
694                }
695            }
696        }
697
698        // Allow empty nodes (DBC spec allows empty BU_: line)
699        let nodes = nodes.unwrap_or_default();
700
701        // If no version was parsed, default to empty version
702        let version = version.or_else(|| {
703            static EMPTY_VERSION: &[u8] = b"VERSION \"\"";
704            let mut parser = Parser::new(EMPTY_VERSION).ok()?;
705            Version::parse(&mut parser).ok()
706        });
707
708        // Build value descriptions map for storage in Dbc
709        #[cfg(any(feature = "alloc", feature = "kernel"))]
710        let value_descriptions_list = {
711            use crate::value_descriptions::ValueDescriptions;
712            use alloc::collections::BTreeMap;
713            let mut map = BTreeMap::new();
714            for (message_id, signal_name, entries) in value_descriptions_buffer {
715                let key = (message_id, signal_name);
716                let value_descriptions = ValueDescriptions::from_slice(&entries);
717                map.insert(key, value_descriptions);
718            }
719            crate::dbc::value_descriptions_list::ValueDescriptionsList::from_map(map)
720        };
721
722        // Convert messages buffer to slice for validation and construction
723        let messages_slice: &[Option<Message<'a>>] = {
724            #[cfg(not(any(feature = "alloc", feature = "kernel")))]
725            {
726                &messages_buffer[..message_count_actual]
727            }
728            #[cfg(any(feature = "alloc", feature = "kernel"))]
729            {
730                &messages_buffer[..]
731            }
732        };
733
734        // Validate messages (duplicate IDs, sender in nodes, etc.)
735        #[cfg(any(feature = "alloc", feature = "kernel"))]
736        {
737            Self::validate(
738                &nodes,
739                messages_slice,
740                message_count_actual,
741                Some(&value_descriptions_list),
742            )?;
743        }
744        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
745        {
746            Self::validate(&nodes, messages_slice, message_count_actual)?;
747        }
748
749        // Construct directly (validation already done)
750        #[cfg(any(feature = "alloc", feature = "kernel"))]
751        {
752            Ok(Self::new_from_options_with_value_descriptions(
753                version,
754                nodes,
755                messages_slice,
756                message_count_actual,
757                value_descriptions_list,
758            ))
759        }
760        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
761        {
762            Ok(Self::new_from_options(
763                version,
764                nodes,
765                messages_slice,
766                message_count_actual,
767            ))
768        }
769    }
770
771    /// Parse a DBC file from a byte slice
772    ///
773    /// # Examples
774    ///
775    /// ```rust,no_run
776    /// use dbc_rs::Dbc;
777    ///
778    /// let dbc_bytes = b"VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM";
779    /// let dbc = Dbc::parse_bytes(dbc_bytes)?;
780    /// println!("Parsed {} messages", dbc.messages().len());
781    /// # Ok::<(), dbc_rs::Error>(())
782    /// ```
783    #[cfg(feature = "alloc")]
784    pub fn parse_bytes(data: &[u8]) -> Result<Dbc<'static>> {
785        let content = core::str::from_utf8(data)
786            .map_err(|_e| Error::dbc(crate::error::lang::INVALID_UTF8))?;
787        // Convert to owned string, box it, and leak to get 'static lifetime
788        use alloc::boxed::Box;
789        let owned = String::from(content);
790        let boxed = owned.into_boxed_str();
791        let content_ref: &'static str = Box::leak(boxed);
792        Dbc::parse(content_ref).map_err(Error::ParseError)
793    }
794
795    /// Parse a DBC file from a file path
796    ///
797    /// # Examples
798    ///
799    /// ```rust,no_run,ignore
800    /// use dbc_rs::Dbc;
801    ///
802    /// // Create a temporary file for the example
803    /// let dbc_content = r#"VERSION "1.0"
804    ///
805    /// BU_: ECM
806    ///
807    /// BO_ 256 Engine : 8 ECM
808    ///  SG_ Signal1 : 0|8@1+ (1,0) [0|255] ""
809    /// "#;
810    /// std::fs::write("/tmp/example.dbc", dbc_content)?;
811    ///
812    /// let dbc = Dbc::from_file("/tmp/example.dbc")?;
813    /// println!("Parsed {} messages", dbc.messages().len());
814    /// # Ok::<(), Box<dyn std::error::Error>>(())
815    /// ```
816    #[cfg(feature = "std")]
817    pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Dbc<'static>> {
818        let file =
819            std::fs::File::open(path).map_err(|_e| Error::dbc(crate::error::lang::READ_FAILED))?;
820        Self::from_reader(file)
821    }
822
823    /// Parse a DBC file from a reader
824    ///
825    /// # Examples
826    ///
827    /// ```rust,no_run
828    /// use dbc_rs::Dbc;
829    /// use std::io::Cursor;
830    ///
831    /// let data = b"VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM";
832    /// let reader = Cursor::new(data);
833    /// let dbc = Dbc::from_reader(reader)?;
834    /// println!("Parsed {} messages", dbc.messages().len());
835    /// # Ok::<(), dbc_rs::Error>(())
836    /// ```
837    #[cfg(feature = "std")]
838    pub fn from_reader<R: std::io::Read>(mut reader: R) -> Result<Dbc<'static>> {
839        let mut buffer = String::new();
840        std::io::Read::read_to_string(&mut reader, &mut buffer)
841            .map_err(|_e| Error::dbc(crate::error::lang::READ_FAILED))?;
842        // Convert to boxed str and leak to get 'static lifetime
843        // The leaked memory will live for the duration of the program
844        use alloc::boxed::Box;
845        let boxed = buffer.into_boxed_str();
846        let content_ref: &'static str = Box::leak(boxed);
847        Dbc::parse(content_ref).map_err(Error::ParseError)
848    }
849
850    /// Serialize this DBC to a DBC format string
851    ///
852    /// # Examples
853    ///
854    /// ```rust,no_run
855    /// use dbc_rs::Dbc;
856    ///
857    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
858    /// let dbc_string = dbc.to_dbc_string();
859    /// // The string can be written to a file or used elsewhere
860    /// assert!(dbc_string.contains("VERSION"));
861    /// # Ok::<(), dbc_rs::Error>(())
862    /// ```
863    #[cfg(feature = "alloc")]
864    #[must_use]
865    pub fn to_dbc_string(&self) -> alloc::string::String {
866        use alloc::string::String;
867        // Pre-allocate with estimated capacity
868        // Estimate: ~50 chars per message + ~100 chars per signal
869        let signal_count: usize = self.messages().iter().map(|m| m.signals().len()).sum();
870        let estimated_capacity = 200 + (self.messages.len() * 50) + (signal_count * 100);
871        let mut result = String::with_capacity(estimated_capacity);
872
873        // VERSION line
874        if let Some(version) = &self.version {
875            result.push_str(&version.to_dbc_string());
876            result.push_str("\n\n");
877        }
878
879        // BU_ line
880        result.push_str(&self.nodes.to_dbc_string());
881        result.push('\n');
882
883        // BO_ and SG_ lines for each message
884        for message in self.messages().iter() {
885            result.push('\n');
886            result.push_str(&message.to_dbc_string_with_signals());
887        }
888
889        result
890    }
891}
892
893#[cfg(feature = "alloc")]
894impl<'a> core::fmt::Display for Dbc<'a> {
895    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
896        write!(f, "{}", self.to_dbc_string())
897    }
898}
899
900#[cfg(all(test, feature = "std"))]
901mod tests {
902    #![allow(clippy::float_cmp)]
903    use super::*;
904    use crate::{
905        Error,
906        error::{ParseError, lang},
907    };
908
909    #[test]
910    fn parses_real_dbc() {
911        let data = r#"VERSION "1.0"
912
913BU_: ECM TCM
914
915BO_ 256 Engine : 8 ECM
916 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
917 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
918
919BO_ 512 Brake : 4 TCM
920 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
921
922        let dbc = Dbc::parse(data).unwrap();
923        assert_eq!(dbc.messages().len(), 2);
924        let mut messages_iter = dbc.messages().iter();
925        let msg0 = messages_iter.next().unwrap();
926        assert_eq!(msg0.signals().len(), 2);
927        let mut signals_iter = msg0.signals().iter();
928        assert_eq!(signals_iter.next().unwrap().name(), "RPM");
929        assert_eq!(signals_iter.next().unwrap().name(), "Temp");
930        let msg1 = messages_iter.next().unwrap();
931        assert_eq!(msg1.signals().len(), 1);
932        assert_eq!(msg1.signals().iter().next().unwrap().name(), "Pressure");
933    }
934
935    #[test]
936    fn test_parse_duplicate_message_id() {
937        // Test that parse also validates duplicate message IDs
938        let data = r#"VERSION "1.0"
939
940BU_: ECM
941
942BO_ 256 EngineData1 : 8 ECM
943 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
944
945BO_ 256 EngineData2 : 8 ECM
946 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
947"#;
948
949        let result = Dbc::parse(data);
950        assert!(result.is_err());
951        match result.unwrap_err() {
952            ParseError::Message(msg) => {
953                assert!(msg.contains(lang::DUPLICATE_MESSAGE_ID));
954            }
955            _ => panic!("Expected ParseError::Message"),
956        }
957    }
958
959    #[test]
960    fn test_parse_sender_not_in_nodes() {
961        // Test that parse also validates message senders are in nodes list
962        let data = r#"VERSION "1.0"
963
964BU_: ECM
965
966BO_ 256 EngineData : 8 TCM
967 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
968"#;
969
970        let result = Dbc::parse(data);
971        assert!(result.is_err());
972        match result.unwrap_err() {
973            ParseError::Message(msg) => {
974                assert!(msg.contains(lang::SENDER_NOT_IN_NODES));
975            }
976            _ => panic!("Expected ParseError::Message"),
977        }
978    }
979
980    #[test]
981    fn test_parse_empty_file() {
982        // Test parsing an empty file
983        let result = Dbc::parse("");
984        assert!(result.is_err());
985        match result.unwrap_err() {
986            ParseError::UnexpectedEof => {
987                // Empty file should result in unexpected EOF
988            }
989            _ => panic!("Expected ParseError::UnexpectedEof"),
990        }
991    }
992
993    #[test]
994    fn test_parse_missing_nodes() {
995        // Test parsing without BU_ statement
996        // Note: The parser allows missing BU_ line and treats it as empty nodes
997        // This is consistent with allowing empty nodes per DBC spec
998        let data = r#"VERSION "1.0"
999
1000BO_ 256 EngineData : 8 ECM
1001 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
1002"#;
1003
1004        let result = Dbc::parse(data);
1005        // Parser should succeed with empty nodes (missing BU_ is treated as empty nodes)
1006        assert!(result.is_ok());
1007        let dbc = result.unwrap();
1008        assert!(dbc.nodes().is_empty());
1009    }
1010
1011    #[test]
1012    fn test_parse_bytes() {
1013        let data = r#"VERSION "1.0"
1014
1015BU_: ECM
1016
1017BO_ 256 Engine : 8 ECM
1018 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1019"#;
1020
1021        let bytes = data.as_bytes();
1022        let dbc = Dbc::parse_bytes(bytes).unwrap();
1023        assert_eq!(
1024            dbc.version().map(|v| v.to_string()),
1025            Some("1.0".to_string())
1026        );
1027        assert_eq!(dbc.messages().len(), 1);
1028    }
1029
1030    #[test]
1031    #[cfg(feature = "alloc")]
1032    fn test_parse_from_string() {
1033        let data = String::from(
1034            r#"VERSION "1.0"
1035
1036BU_: ECM
1037
1038BO_ 256 Engine : 8 ECM
1039 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1040"#,
1041        );
1042
1043        let dbc = Dbc::parse(&data).unwrap();
1044        assert_eq!(
1045            dbc.version().map(|v| v.to_string()),
1046            Some("1.0".to_string())
1047        );
1048        assert_eq!(dbc.messages().len(), 1);
1049    }
1050
1051    #[test]
1052    fn test_parse_bytes_invalid_utf8() {
1053        // Invalid UTF-8 sequence
1054        let invalid_bytes = &[0xFF, 0xFE, 0xFD];
1055        let result = Dbc::parse_bytes(invalid_bytes);
1056        assert!(result.is_err());
1057        match result.unwrap_err() {
1058            Error::Dbc(msg) => {
1059                assert!(msg.contains(lang::INVALID_UTF8));
1060            }
1061            _ => panic!("Expected Dbc error"),
1062        }
1063    }
1064
1065    #[test]
1066    #[cfg(feature = "alloc")]
1067    fn test_save_basic() {
1068        // Use parsing instead of builders
1069        let dbc_content = r#"VERSION "1.0"
1070
1071BU_: ECM
1072
1073BO_ 256 EngineData : 8 ECM
1074 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
1075"#;
1076        let dbc = Dbc::parse(dbc_content).unwrap();
1077
1078        let saved = dbc.to_dbc_string();
1079        assert!(saved.contains("VERSION \"1.0\""));
1080        assert!(saved.contains("BU_: ECM"));
1081        assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
1082        assert!(saved.contains("SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *")); // BigEndian = @0
1083    }
1084
1085    #[test]
1086    fn test_save_round_trip() {
1087        let original = r#"VERSION "1.0"
1088
1089BU_: ECM TCM
1090
1091BO_ 256 EngineData : 8 ECM
1092 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
1093 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
1094
1095BO_ 512 BrakeData : 4 TCM
1096 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
1097"#;
1098
1099        let dbc = Dbc::parse(original).unwrap();
1100        let saved = dbc.to_dbc_string();
1101        let dbc2 = Dbc::parse(&saved).unwrap();
1102
1103        // Verify round-trip: parsed data should match
1104        assert_eq!(
1105            dbc.version().map(|v| v.to_string()),
1106            dbc2.version().map(|v| v.to_string())
1107        );
1108        assert_eq!(dbc.messages().len(), dbc2.messages().len());
1109
1110        for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
1111            assert_eq!(msg1.id(), msg2.id());
1112            assert_eq!(msg1.name(), msg2.name());
1113            assert_eq!(msg1.dlc(), msg2.dlc());
1114            assert_eq!(msg1.sender(), msg2.sender());
1115            assert_eq!(msg1.signals().len(), msg2.signals().len());
1116
1117            for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
1118                assert_eq!(sig1.name(), sig2.name());
1119                assert_eq!(sig1.start_bit(), sig2.start_bit());
1120                assert_eq!(sig1.length(), sig2.length());
1121                assert_eq!(sig1.byte_order(), sig2.byte_order());
1122                assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
1123                assert_eq!(sig1.factor(), sig2.factor());
1124                assert_eq!(sig1.offset(), sig2.offset());
1125                assert_eq!(sig1.min(), sig2.min());
1126                assert_eq!(sig1.max(), sig2.max());
1127                assert_eq!(sig1.unit(), sig2.unit());
1128                assert_eq!(sig1.receivers(), sig2.receivers());
1129            }
1130        }
1131    }
1132
1133    #[test]
1134    #[cfg(feature = "alloc")]
1135    fn test_save_multiple_messages() {
1136        // Use parsing instead of builders
1137        let dbc_content = r#"VERSION "1.0"
1138
1139BU_: ECM TCM
1140
1141BO_ 256 EngineData : 8 ECM
1142 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
1143
1144BO_ 512 BrakeData : 4 TCM
1145 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar"
1146"#;
1147        let dbc = Dbc::parse(dbc_content).unwrap();
1148        let saved = dbc.to_dbc_string();
1149
1150        // Verify both messages are present
1151        assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
1152        assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
1153        assert!(saved.contains("SG_ RPM"));
1154        assert!(saved.contains("SG_ Pressure"));
1155    }
1156
1157    // Note: Builder limit tests have been moved to dbc_builder.rs
1158    // These tests require building many messages programmatically, which is builder functionality
1159
1160    #[test]
1161    fn test_parse_without_version() {
1162        // DBC file without VERSION line should default to empty version
1163        let data = r#"
1164BU_: ECM
1165
1166BO_ 256 Engine : 8 ECM
1167 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1168"#;
1169        let dbc = Dbc::parse(data).unwrap();
1170        assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
1171    }
1172
1173    #[test]
1174    fn test_parse_without_version_with_comment() {
1175        // DBC file with comment and no VERSION line
1176        let data = r#"// This is a comment
1177BU_: ECM
1178
1179BO_ 256 Engine : 8 ECM
1180 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1181"#;
1182        let dbc = Dbc::parse(data).unwrap();
1183        assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
1184    }
1185
1186    #[test]
1187    fn test_parse_error_with_line_number() {
1188        // Test that errors include line numbers (or at least that errors are returned)
1189        let data = r#"VERSION "1.0"
1190
1191BU_: ECM
1192
1193BO_ 256 Engine : 8 ECM
1194 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1195BO_ 257 Invalid : 8 ECM
1196 SG_ InvalidSignal : invalid|16@1+ (0.25,0) [0|8000] "rpm"
1197"#;
1198        let result = Dbc::parse(data);
1199        assert!(result.is_err());
1200        let err = result.unwrap_err();
1201        // Accept any ParseError - line number tracking is not yet implemented
1202        match err {
1203            ParseError::Version(_)
1204            | ParseError::Message(_)
1205            | ParseError::Nodes(_)
1206            | ParseError::Receivers(_)
1207            | ParseError::Signal(_)
1208            | ParseError::UnexpectedEof
1209            | ParseError::Expected(_)
1210            | ParseError::InvalidChar(_)
1211            | ParseError::MaxStrLength(_) => {
1212                // Accept various parse errors
1213            }
1214        };
1215        // Note: Line number tracking is not yet implemented, so we just verify an error is returned
1216    }
1217
1218    #[test]
1219    fn test_parse_error_version_with_line_number() {
1220        // Test that version parsing errors are returned (line number tracking not yet implemented)
1221        let data = r#"VERSION invalid
1222
1223BU_: ECM
1224"#;
1225        let result = Dbc::parse(data);
1226        assert!(result.is_err());
1227        let err = result.unwrap_err();
1228        // Accept any ParseError - line number tracking is not yet implemented
1229        match err {
1230            ParseError::Version(_)
1231            | ParseError::Message(_)
1232            | ParseError::Nodes(_)
1233            | ParseError::Receivers(_)
1234            | ParseError::Signal(_)
1235            | ParseError::UnexpectedEof
1236            | ParseError::Expected(_)
1237            | ParseError::InvalidChar(_)
1238            | ParseError::MaxStrLength(_) => {
1239                // Accept various parse errors
1240            }
1241        };
1242        // Note: Line number tracking is not yet implemented, so we just verify an error is returned
1243    }
1244
1245    #[test]
1246    fn test_parse_with_lenient_boundary_check() {
1247        // Test that lenient mode allows signals that extend beyond message boundaries
1248        let data = r#"VERSION "1.0"
1249
1250BU_: ECM
1251
1252BO_ 256 Test : 8 ECM
1253 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
1254"#;
1255
1256        // Strict mode should fail
1257        let result = Dbc::parse(data);
1258        assert!(result.is_err());
1259
1260        // Lenient mode should succeed
1261        let options = ParseOptions::lenient();
1262        let dbc = Dbc::parse_with_options(data, options).unwrap();
1263        assert_eq!(dbc.messages().len(), 1);
1264        let message = dbc.messages().at(0).unwrap();
1265        assert_eq!(message.signals().len(), 1);
1266        assert_eq!(message.signals().at(0).unwrap().name(), "CHECKSUM");
1267    }
1268
1269    #[test]
1270    fn test_parse_with_strict_boundary_check() {
1271        // Test that strict mode (default) rejects signals that extend beyond boundaries
1272        let data = r#"VERSION "1.0"
1273
1274BU_: ECM
1275
1276BO_ 256 Test : 8 ECM
1277 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
1278"#;
1279
1280        // Default (strict) mode should fail
1281        let result = Dbc::parse(data);
1282        assert!(result.is_err());
1283
1284        // Explicit strict mode should also fail
1285        let options = ParseOptions::new();
1286        let result = Dbc::parse_with_options(data, options);
1287        assert!(result.is_err());
1288    }
1289
1290    #[test]
1291    #[cfg(any(feature = "alloc", feature = "kernel"))]
1292    fn test_parse_val_value_descriptions() {
1293        let data = r#"VERSION ""
1294
1295NS_ :
1296
1297BS_:
1298
1299BU_: Node1 Node2
1300
1301BO_ 100 Message1 : 8 Node1
1302 SG_ Signal : 32|8@1- (1,0) [-1|4] "Gear" Node2
1303
1304VAL_ 100 Signal -1 "Reverse" 0 "Neutral" 1 "First" 2 "Second" 3 "Third" 4 "Fourth" ;
1305"#;
1306
1307        let dbc = match Dbc::parse(data) {
1308            Ok(dbc) => dbc,
1309            Err(e) => panic!("Failed to parse DBC: {:?}", e),
1310        };
1311
1312        // Verify basic structure
1313        assert_eq!(dbc.messages().len(), 1);
1314        let message = dbc.messages().iter().find(|m| m.id() == 100).unwrap();
1315        assert_eq!(message.name(), "Message1");
1316        assert_eq!(message.sender(), "Node1");
1317
1318        // Verify signal exists
1319        let signal = message.signals().find("Signal").unwrap();
1320        assert_eq!(signal.name(), "Signal");
1321        assert_eq!(signal.start_bit(), 32);
1322        assert_eq!(signal.length(), 8);
1323        assert_eq!(signal.unit(), Some("Gear"));
1324
1325        // Verify value descriptions are parsed and accessible
1326        let value_descriptions = dbc
1327            .value_descriptions_for_signal(100, "Signal")
1328            .expect("Value descriptions should exist for signal");
1329
1330        // Verify all value mappings
1331        assert_eq!(value_descriptions.get(0xFFFFFFFF), Some("Reverse")); // -1 as u32
1332        assert_eq!(value_descriptions.get(0), Some("Neutral"));
1333        assert_eq!(value_descriptions.get(1), Some("First"));
1334        assert_eq!(value_descriptions.get(2), Some("Second"));
1335        assert_eq!(value_descriptions.get(3), Some("Third"));
1336        assert_eq!(value_descriptions.get(4), Some("Fourth"));
1337
1338        // Verify non-existent values return None
1339        assert_eq!(value_descriptions.get(5), None);
1340        assert_eq!(value_descriptions.get(99), None);
1341
1342        // Verify we can iterate over all value descriptions
1343        let iter = value_descriptions.iter();
1344        let mut entries: Vec<_> = iter.collect();
1345        entries.sort_by_key(|(v, _)| *v);
1346
1347        assert_eq!(entries.len(), 6);
1348        // After sorting by key (u64), 0xFFFFFFFF (4294967295) comes after smaller values
1349        assert_eq!(entries[0], (0, "Neutral"));
1350        assert_eq!(entries[1], (1, "First"));
1351        assert_eq!(entries[2], (2, "Second"));
1352        assert_eq!(entries[3], (3, "Third"));
1353        assert_eq!(entries[4], (4, "Fourth"));
1354        assert_eq!(entries[5], (0xFFFFFFFF, "Reverse"));
1355    }
1356
1357    #[test]
1358    #[cfg(any(feature = "alloc", feature = "kernel"))]
1359    fn test_parse_val_global_value_descriptions() {
1360        // Test global value descriptions (VAL_ -1) that apply to all signals with the same name
1361        let data = r#"VERSION "1.0"
1362
1363NS_ :
1364
1365    VAL_
1366
1367BS_:
1368
1369BU_: ECU DASH
1370
1371BO_ 256 EngineData: 8 ECU
1372 SG_ EngineRPM : 0|16@1+ (0.125,0) [0|8000] "rpm" Vector__XXX
1373 SG_ DI_gear : 24|3@1+ (1,0) [0|7] "" Vector__XXX
1374
1375BO_ 512 DashboardDisplay: 8 DASH
1376 SG_ DI_gear : 0|3@1+ (1,0) [0|7] "" Vector__XXX
1377 SG_ SpeedDisplay : 8|16@1+ (0.01,0) [0|300] "km/h" Vector__XXX
1378
1379VAL_ -1 DI_gear 0 "INVALID" 1 "P" 2 "R" 3 "N" 4 "D" 5 "S" 6 "L" 7 "SNA" ;
1380"#;
1381
1382        let dbc = match Dbc::parse(data) {
1383            Ok(dbc) => dbc,
1384            Err(e) => panic!("Failed to parse DBC: {:?}", e),
1385        };
1386
1387        // Verify basic structure
1388        assert_eq!(dbc.messages().len(), 2);
1389
1390        // Verify first message (EngineData)
1391        let engine_msg = dbc.messages().iter().find(|m| m.id() == 256).unwrap();
1392        assert_eq!(engine_msg.name(), "EngineData");
1393        assert_eq!(engine_msg.sender(), "ECU");
1394        let di_gear_signal1 = engine_msg.signals().find("DI_gear").unwrap();
1395        assert_eq!(di_gear_signal1.name(), "DI_gear");
1396        assert_eq!(di_gear_signal1.start_bit(), 24);
1397
1398        // Verify second message (DashboardDisplay)
1399        let dash_msg = dbc.messages().iter().find(|m| m.id() == 512).unwrap();
1400        assert_eq!(dash_msg.name(), "DashboardDisplay");
1401        assert_eq!(dash_msg.sender(), "DASH");
1402        let di_gear_signal2 = dash_msg.signals().find("DI_gear").unwrap();
1403        assert_eq!(di_gear_signal2.name(), "DI_gear");
1404        assert_eq!(di_gear_signal2.start_bit(), 0);
1405
1406        // Verify global value descriptions apply to DI_gear in message 256
1407        let value_descriptions1 = dbc
1408            .value_descriptions_for_signal(256, "DI_gear")
1409            .expect("Global value descriptions should exist for DI_gear in message 256");
1410
1411        assert_eq!(value_descriptions1.get(0), Some("INVALID"));
1412        assert_eq!(value_descriptions1.get(1), Some("P"));
1413        assert_eq!(value_descriptions1.get(2), Some("R"));
1414        assert_eq!(value_descriptions1.get(3), Some("N"));
1415        assert_eq!(value_descriptions1.get(4), Some("D"));
1416        assert_eq!(value_descriptions1.get(5), Some("S"));
1417        assert_eq!(value_descriptions1.get(6), Some("L"));
1418        assert_eq!(value_descriptions1.get(7), Some("SNA"));
1419
1420        // Verify global value descriptions also apply to DI_gear in message 512
1421        let value_descriptions2 = dbc
1422            .value_descriptions_for_signal(512, "DI_gear")
1423            .expect("Global value descriptions should exist for DI_gear in message 512");
1424
1425        // Both should return the same value descriptions (same reference or same content)
1426        assert_eq!(value_descriptions2.get(0), Some("INVALID"));
1427        assert_eq!(value_descriptions2.get(1), Some("P"));
1428        assert_eq!(value_descriptions2.get(2), Some("R"));
1429        assert_eq!(value_descriptions2.get(3), Some("N"));
1430        assert_eq!(value_descriptions2.get(4), Some("D"));
1431        assert_eq!(value_descriptions2.get(5), Some("S"));
1432        assert_eq!(value_descriptions2.get(6), Some("L"));
1433        assert_eq!(value_descriptions2.get(7), Some("SNA"));
1434
1435        // Verify they should be the same instance (both reference the global entry)
1436        // Since we store by (Option<u32>, &str), both should return the same entry
1437        assert_eq!(value_descriptions1.len(), value_descriptions2.len());
1438        assert_eq!(value_descriptions1.len(), 8);
1439
1440        // Verify other signals don't have value descriptions
1441        assert_eq!(dbc.value_descriptions_for_signal(256, "EngineRPM"), None);
1442        assert_eq!(dbc.value_descriptions_for_signal(512, "SpeedDisplay"), None);
1443    }
1444}