dbc_rs/dbc/
parse.rs

1use crate::{
2    Dbc, Error, ExtendedMultiplexing, MAX_EXTENDED_MULTIPLEXING, MAX_MESSAGES,
3    MAX_SIGNALS_PER_MESSAGE, Message, Nodes, Parser, Result, Signal, Version,
4    compat::Vec,
5    dbc::{Messages, Validate},
6};
7#[cfg(feature = "std")]
8use crate::{ValueDescriptions, dbc::ValueDescriptionsMap};
9#[cfg(feature = "std")]
10use std::collections::BTreeMap;
11
12impl Dbc {
13    /// Parse a DBC file from a string slice
14    ///
15    /// # Examples
16    ///
17    /// ```rust,no_run
18    /// use dbc_rs::Dbc;
19    ///
20    /// let dbc_content = r#"VERSION "1.0"
21    ///
22    /// BU_: ECM
23    ///
24    /// BO_ 256 EngineData : 8 ECM
25    ///  SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm""#;
26    ///
27    /// let dbc = Dbc::parse(dbc_content)?;
28    /// assert_eq!(dbc.messages().len(), 1);
29    /// # Ok::<(), dbc_rs::Error>(())
30    /// ```
31    pub fn parse(data: &str) -> Result<Self> {
32        let mut parser = Parser::new(data.as_bytes())?;
33
34        let mut messages_buffer: Vec<Message, { MAX_MESSAGES }> = Vec::new();
35
36        let mut message_count_actual = 0;
37
38        // Parse version, nodes, and messages
39        use crate::{
40            BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SG_MUL_VAL_,
41            SIG_GROUP_, SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
42        };
43
44        let mut version: Option<Version> = None;
45        let mut nodes: Option<Nodes> = None;
46
47        // Store value descriptions during parsing: (message_id, signal_name, value, description)
48        #[cfg(feature = "std")]
49        type ValueDescriptionsBufferEntry = (
50            Option<u32>,
51            std::string::String,
52            std::vec::Vec<(u64, std::string::String)>,
53        );
54        #[cfg(feature = "std")]
55        let mut value_descriptions_buffer: std::vec::Vec<ValueDescriptionsBufferEntry> =
56            std::vec::Vec::new();
57
58        // Store extended multiplexing entries during parsing
59        let mut extended_multiplexing_buffer: Vec<
60            ExtendedMultiplexing,
61            { MAX_EXTENDED_MULTIPLEXING },
62        > = Vec::new();
63
64        loop {
65            // Skip comments (lines starting with //)
66            parser.skip_newlines_and_spaces();
67            if parser.starts_with(b"//") {
68                parser.skip_to_end_of_line();
69                continue;
70            }
71
72            let keyword_result = parser.peek_next_keyword();
73            let keyword = match keyword_result {
74                Ok(kw) => kw,
75                Err(Error::UnexpectedEof) => break,
76                Err(Error::Expected(_)) => {
77                    if parser.starts_with(b"//") {
78                        parser.skip_to_end_of_line();
79                        continue;
80                    }
81                    return Err(keyword_result.unwrap_err());
82                }
83                Err(e) => return Err(e),
84            };
85
86            // Save position after peek_next_keyword (which skips whitespace, so we're at the keyword)
87            let pos_at_keyword = parser.pos();
88
89            match keyword {
90                NS_ => {
91                    // Consume NS_ keyword
92                    parser
93                        .expect(crate::NS_.as_bytes())
94                        .map_err(|_| Error::Expected("Failed to consume NS_ keyword"))?;
95                    parser.skip_newlines_and_spaces();
96                    let _ = parser.expect(b":").ok();
97                    loop {
98                        parser.skip_newlines_and_spaces();
99                        if parser.is_empty() {
100                            break;
101                        }
102                        if parser.starts_with(b" ") || parser.starts_with(b"\t") {
103                            parser.skip_to_end_of_line();
104                            continue;
105                        }
106                        if parser.starts_with(b"//") {
107                            parser.skip_to_end_of_line();
108                            continue;
109                        }
110                        if parser.starts_with(BS_.as_bytes())
111                            || parser.starts_with(BU_.as_bytes())
112                            || parser.starts_with(BO_.as_bytes())
113                            || parser.starts_with(SG_.as_bytes())
114                            || parser.starts_with(VERSION.as_bytes())
115                        {
116                            break;
117                        }
118                        parser.skip_to_end_of_line();
119                    }
120                    continue;
121                }
122                CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | SIG_GROUP_
123                | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
124                    // Consume keyword then skip to end of line
125                    let _ = parser.expect(keyword.as_bytes()).ok();
126                    parser.skip_to_end_of_line();
127                    continue;
128                }
129                SG_MUL_VAL_ => {
130                    // Consume SG_MUL_VAL_ keyword
131                    parser
132                        .expect(SG_MUL_VAL_.as_bytes())
133                        .map_err(|_| Error::Expected("Failed to consume SG_MUL_VAL_ keyword"))?;
134
135                    // Parse the extended multiplexing entry
136                    if let Some(ext_mux) = ExtendedMultiplexing::parse(&mut parser) {
137                        if extended_multiplexing_buffer.push(ext_mux).is_err() {
138                            // Buffer full - return error instead of silently dropping entries
139                            return Err(Error::Validation(Error::EXTENDED_MULTIPLEXING_TOO_MANY));
140                        }
141                    } else {
142                        // Parsing failed, skip to end of line
143                        parser.skip_to_end_of_line();
144                    }
145                    continue;
146                }
147                VAL_ => {
148                    #[cfg(feature = "std")]
149                    {
150                        // Consume VAL_ keyword
151                        let _ = parser.expect(crate::VAL_.as_bytes()).ok();
152                        // Parse VAL_ statement: VAL_ message_id signal_name value1 "desc1" value2 "desc2" ... ;
153                        // Note: message_id of -1 (0xFFFFFFFF) means the value descriptions apply to
154                        // all signals with this name in ANY message (global value descriptions)
155                        parser.skip_newlines_and_spaces();
156                        let message_id = match parser.parse_i64() {
157                            Ok(id) => {
158                                // -1 (0xFFFFFFFF) is the magic number for global value descriptions
159                                if id == -1 {
160                                    None
161                                } else if id >= 0 && id <= u32::MAX as i64 {
162                                    Some(id as u32)
163                                } else {
164                                    parser.skip_to_end_of_line();
165                                    continue;
166                                }
167                            }
168                            Err(_) => {
169                                parser.skip_to_end_of_line();
170                                continue;
171                            }
172                        };
173                        parser.skip_newlines_and_spaces();
174                        let signal_name = match parser.parse_identifier() {
175                            Ok(name) => name.to_string(),
176                            Err(_) => {
177                                parser.skip_to_end_of_line();
178                                continue;
179                            }
180                        };
181                        // Parse value-description pairs
182                        let mut entries: std::vec::Vec<(u64, std::string::String)> =
183                            std::vec::Vec::new();
184                        loop {
185                            parser.skip_newlines_and_spaces();
186                            // Check for semicolon (end of VAL_ statement)
187                            if parser.starts_with(b";") {
188                                parser.expect(b";").ok();
189                                break;
190                            }
191                            // Parse value (as i64 first to handle negative values like -1, then convert to u64)
192                            // Note: -1 (0xFFFFFFFF) is the magic number for global value descriptions in message_id,
193                            // but values in VAL_ can also be negative
194                            let value = match parser.parse_i64() {
195                                Ok(v) => {
196                                    // Handle -1 specially: convert to 0xFFFFFFFF (u32::MAX) instead of large u64
197                                    if v == -1 { 0xFFFF_FFFFu64 } else { v as u64 }
198                                }
199                                Err(_) => {
200                                    parser.skip_to_end_of_line();
201                                    break;
202                                }
203                            };
204                            parser.skip_newlines_and_spaces();
205                            // Parse description string (expect quote, then take until quote)
206                            if parser.expect(b"\"").is_err() {
207                                parser.skip_to_end_of_line();
208                                break;
209                            }
210                            let description_bytes = match parser.take_until_quote(false, 1024) {
211                                Ok(bytes) => bytes,
212                                Err(_) => {
213                                    parser.skip_to_end_of_line();
214                                    break;
215                                }
216                            };
217                            let description = match core::str::from_utf8(description_bytes) {
218                                Ok(s) => s.to_string(),
219                                Err(_) => {
220                                    parser.skip_to_end_of_line();
221                                    break;
222                                }
223                            };
224                            entries.push((value, description));
225                        }
226                        if !entries.is_empty() {
227                            value_descriptions_buffer.push((message_id, signal_name, entries));
228                        }
229                    }
230                    #[cfg(not(feature = "std"))]
231                    {
232                        // In no_std mode, consume VAL_ keyword and skip the rest
233                        let _ = parser.expect(crate::VAL_.as_bytes()).ok();
234                        parser.skip_to_end_of_line();
235                    }
236                    continue;
237                }
238                VERSION => {
239                    // Version::parse expects VERSION keyword, don't consume it here
240                    version = Some(Version::parse(&mut parser)?);
241                    continue;
242                }
243                BU_ => {
244                    // Nodes::parse expects BU_ keyword, create parser from original input including it
245                    parser.skip_to_end_of_line();
246                    let bu_input = &data.as_bytes()[pos_at_keyword..parser.pos()];
247                    let mut bu_parser = Parser::new(bu_input)?;
248                    nodes = Some(Nodes::parse(&mut bu_parser)?);
249                    continue;
250                }
251                BO_ => {
252                    // Check limit using MAX_MESSAGES constant
253                    if message_count_actual >= MAX_MESSAGES {
254                        return Err(Error::Nodes(Error::NODES_TOO_MANY));
255                    }
256
257                    // Save parser position (at BO_ keyword, so Message::parse can consume it)
258                    let message_start_pos = pos_at_keyword;
259
260                    // Don't manually parse - just find where the header ends by looking for the colon and sender
261                    // We need to find the end of the header line to separate it from signals
262                    let header_line_end = {
263                        // Skip to end of line to find where header ends
264                        let mut temp_parser = Parser::new(&data.as_bytes()[pos_at_keyword..])?;
265                        // Skip BO_ keyword
266                        temp_parser.expect(crate::BO_.as_bytes()).ok();
267                        temp_parser.skip_whitespace().ok();
268                        temp_parser.parse_u32().ok(); // ID
269                        temp_parser.skip_whitespace().ok();
270                        temp_parser.parse_identifier().ok(); // name
271                        temp_parser.skip_whitespace().ok();
272                        temp_parser.expect(b":").ok(); // colon
273                        temp_parser.skip_whitespace().ok();
274                        temp_parser.parse_u8().ok(); // DLC
275                        temp_parser.skip_whitespace().ok();
276                        temp_parser.parse_identifier().ok(); // sender
277                        pos_at_keyword + temp_parser.pos()
278                    };
279
280                    // Now parse signals from the original parser
281                    parser.skip_to_end_of_line(); // Skip past header line
282
283                    let mut signals_array: Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }> = Vec::new();
284
285                    // Parse signals until we find a non-signal line
286                    loop {
287                        parser.skip_newlines_and_spaces();
288
289                        // Use peek_next_keyword to check for SG_ keyword
290                        // peek_next_keyword correctly distinguishes SG_ from SG_MUL_VAL_ (checks longer keywords first)
291                        let keyword_result = parser.peek_next_keyword();
292                        let keyword = match keyword_result {
293                            Ok(kw) => kw,
294                            Err(Error::UnexpectedEof) => break,
295                            Err(_) => break, // Not a keyword, no more signals
296                        };
297
298                        // Only process SG_ signals here (SG_MUL_VAL_ is handled in main loop)
299                        if keyword != SG_ {
300                            break; // Not a signal, exit signal parsing loop
301                        }
302
303                        // Check limit before parsing
304                        if signals_array.len() >= MAX_SIGNALS_PER_MESSAGE {
305                            return Err(Error::Message(Error::MESSAGE_TOO_MANY_SIGNALS));
306                        }
307
308                        // Parse signal - Signal::parse consumes SG_ itself
309                        match Signal::parse(&mut parser) {
310                            Ok(signal) => {
311                                signals_array.push(signal).map_err(|_| {
312                                    Error::Receivers(Error::SIGNAL_RECEIVERS_TOO_MANY)
313                                })?;
314                                // Receivers::parse stops at newline but doesn't consume it
315                                // Consume it so next iteration starts at the next line
316                                if parser.at_newline() {
317                                    parser.skip_to_end_of_line();
318                                }
319                            }
320                            Err(_) => {
321                                // Parsing failed, skip to end of line and stop
322                                parser.skip_to_end_of_line();
323                                break;
324                            }
325                        }
326                    }
327
328                    // Restore parser to start of message line and use Message::parse
329                    // Create a new parser from the original input, but only up to the end of the header
330                    // (not including signals, so Message::parse doesn't complain about extra content)
331                    let message_input = &data.as_bytes()[message_start_pos..header_line_end];
332                    let mut message_parser = Parser::new(message_input)?;
333
334                    // Use Message::parse which will parse the header and use our signals
335                    let message = Message::parse(&mut message_parser, signals_array.as_slice())?;
336
337                    messages_buffer
338                        .push(message)
339                        .map_err(|_| Error::Message(Error::NODES_TOO_MANY))?;
340                    message_count_actual += 1;
341                    continue;
342                }
343                SG_ => {
344                    // Orphaned signal (not inside a message) - skip it
345                    parser.skip_to_end_of_line();
346                    continue;
347                }
348                _ => {
349                    parser.skip_to_end_of_line();
350                    continue;
351                }
352            }
353        }
354
355        // Allow empty nodes (DBC spec allows empty BU_: line)
356        let nodes = nodes.unwrap_or_default();
357
358        // If no version was parsed, default to empty version
359        let version = version.or_else(|| {
360            static EMPTY_VERSION: &[u8] = b"VERSION \"\"";
361            let mut parser = Parser::new(EMPTY_VERSION).ok()?;
362            Version::parse(&mut parser).ok()
363        });
364
365        // Build value descriptions map for storage in Dbc
366        #[cfg(feature = "std")]
367        let value_descriptions_map = {
368            let mut map: BTreeMap<(Option<u32>, std::string::String), ValueDescriptions> =
369                BTreeMap::new();
370            for (message_id, signal_name, entries) in value_descriptions_buffer {
371                let key = (message_id, signal_name);
372                let value_descriptions = ValueDescriptions::from_slice(&entries);
373                map.insert(key, value_descriptions);
374            }
375            ValueDescriptionsMap::from_map(map)
376        };
377
378        // Convert messages buffer to slice for validation and construction
379        let messages_slice: &[Message] = messages_buffer.as_slice();
380
381        // Validate messages (duplicate IDs, sender in nodes, etc.)
382        #[cfg(feature = "std")]
383        Validate::validate(&nodes, messages_slice, Some(&value_descriptions_map)).map_err(|e| {
384            crate::error::map_val_error(e, Error::Message, || {
385                Error::Message(Error::MESSAGE_ERROR_PREFIX)
386            })
387        })?;
388        #[cfg(not(feature = "std"))]
389        Validate::validate(&nodes, messages_slice).map_err(|e| {
390            crate::error::map_val_error(e, Error::Message, || {
391                Error::Message(Error::MESSAGE_ERROR_PREFIX)
392            })
393        })?;
394
395        // Construct directly (validation already done)
396        let messages = Messages::new(messages_slice)?;
397        #[cfg(feature = "std")]
398        let dbc = Dbc::new(
399            version,
400            nodes,
401            messages,
402            value_descriptions_map,
403            extended_multiplexing_buffer,
404        );
405        #[cfg(not(feature = "std"))]
406        let dbc = Dbc::new(version, nodes, messages, extended_multiplexing_buffer);
407        Ok(dbc)
408    }
409
410    /// Parse a DBC file from a byte slice
411    ///
412    /// # Examples
413    ///
414    /// ```rust,no_run
415    /// use dbc_rs::Dbc;
416    ///
417    /// let dbc_bytes = b"VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM";
418    /// let dbc = Dbc::parse_bytes(dbc_bytes)?;
419    /// println!("Parsed {} messages", dbc.messages().len());
420    /// # Ok::<(), dbc_rs::Error>(())
421    /// ```
422    pub fn parse_bytes(data: &[u8]) -> Result<Self> {
423        let content =
424            core::str::from_utf8(data).map_err(|_e| Error::Expected(Error::INVALID_UTF8))?;
425        Dbc::parse(content)
426    }
427}
428
429#[cfg(test)]
430mod tests {
431    use crate::Dbc;
432
433    #[test]
434    fn test_parse_basic() {
435        let dbc_content = r#"VERSION "1.0"
436
437BU_: ECM
438
439BO_ 256 Engine : 8 ECM
440 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
441"#;
442        let dbc = Dbc::parse(dbc_content).unwrap();
443        assert_eq!(dbc.version().map(|v| v.as_str()), Some("1.0"));
444        assert!(dbc.nodes().contains("ECM"));
445        assert_eq!(dbc.messages().len(), 1);
446    }
447
448    #[test]
449    fn test_parse_bytes() {
450        let dbc_bytes = b"VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM";
451        let dbc = Dbc::parse_bytes(dbc_bytes).unwrap();
452        assert_eq!(dbc.version().map(|v| v.as_str()), Some("1.0"));
453        assert!(dbc.nodes().contains("ECM"));
454        assert_eq!(dbc.messages().len(), 1);
455    }
456
457    #[test]
458    fn test_parse_empty_nodes() {
459        let dbc_content = r#"VERSION "1.0"
460
461BU_:
462
463BO_ 256 Engine : 8 ECM
464"#;
465        let dbc = Dbc::parse(dbc_content).unwrap();
466        assert!(dbc.nodes().is_empty());
467    }
468
469    #[test]
470    fn test_parse_no_version() {
471        let dbc_content = r#"BU_: ECM
472
473BO_ 256 Engine : 8 ECM
474"#;
475        let dbc = Dbc::parse(dbc_content).unwrap();
476        // Should default to empty version
477        assert!(dbc.version().is_some());
478    }
479
480    #[test]
481    fn parses_real_dbc() {
482        let data = r#"VERSION "1.0"
483
484BU_: ECM TCM
485
486BO_ 256 Engine : 8 ECM
487 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
488 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
489
490BO_ 512 Brake : 4 TCM
491 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
492
493        let dbc = Dbc::parse(data).unwrap();
494        assert_eq!(dbc.messages().len(), 2);
495        let mut messages_iter = dbc.messages().iter();
496        let msg0 = messages_iter.next().unwrap();
497        assert_eq!(msg0.signals().len(), 2);
498        let mut signals_iter = msg0.signals().iter();
499        assert_eq!(signals_iter.next().unwrap().name(), "RPM");
500        assert_eq!(signals_iter.next().unwrap().name(), "Temp");
501        let msg1 = messages_iter.next().unwrap();
502        assert_eq!(msg1.signals().len(), 1);
503        assert_eq!(msg1.signals().iter().next().unwrap().name(), "Pressure");
504    }
505
506    #[test]
507    fn test_parse_duplicate_message_id() {
508        use crate::Error;
509        // Test that parse also validates duplicate message IDs
510        let data = r#"VERSION "1.0"
511
512BU_: ECM
513
514BO_ 256 EngineData1 : 8 ECM
515 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
516
517BO_ 256 EngineData2 : 8 ECM
518 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
519"#;
520
521        let result = Dbc::parse(data);
522        assert!(result.is_err());
523        match result.unwrap_err() {
524            Error::Message(msg) => {
525                assert!(msg.contains(Error::DUPLICATE_MESSAGE_ID));
526            }
527            _ => panic!("Expected Error::Message"),
528        }
529    }
530
531    #[test]
532    fn test_parse_sender_not_in_nodes() {
533        use crate::Error;
534        // Test that parse also validates message senders are in nodes list
535        let data = r#"VERSION "1.0"
536
537BU_: ECM
538
539BO_ 256 EngineData : 8 TCM
540 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
541"#;
542
543        let result = Dbc::parse(data);
544        assert!(result.is_err());
545        match result.unwrap_err() {
546            Error::Message(msg) => {
547                assert!(msg.contains(Error::SENDER_NOT_IN_NODES));
548            }
549            _ => panic!("Expected Error::Message"),
550        }
551    }
552
553    #[test]
554    fn test_parse_empty_file() {
555        use crate::Error;
556        // Test parsing an empty file
557        let result = Dbc::parse("");
558        assert!(result.is_err());
559        match result.unwrap_err() {
560            Error::UnexpectedEof => {
561                // Empty file should result in unexpected EOF
562            }
563            _ => panic!("Expected Error::UnexpectedEof"),
564        }
565    }
566
567    #[test]
568    fn test_parse_bytes_invalid_utf8() {
569        use crate::Error;
570        // Invalid UTF-8 sequence
571        let invalid_bytes = &[0xFF, 0xFE, 0xFD];
572        let result = Dbc::parse_bytes(invalid_bytes);
573        assert!(result.is_err());
574        match result.unwrap_err() {
575            Error::Expected(msg) => {
576                assert_eq!(msg, Error::INVALID_UTF8);
577            }
578            _ => panic!("Expected Error::Expected with INVALID_UTF8"),
579        }
580    }
581
582    #[test]
583    fn test_parse_without_version_with_comment() {
584        // DBC file with comment and no VERSION line
585        let data = r#"// This is a comment
586BU_: ECM
587
588BO_ 256 Engine : 8 ECM
589 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
590"#;
591        let dbc = Dbc::parse(data).unwrap();
592        assert_eq!(dbc.version().map(|v| v.as_str()), Some(""));
593    }
594
595    #[test]
596    fn test_parse_with_strict_boundary_check() {
597        // Test that strict mode (default) rejects signals that extend beyond boundaries
598        let data = r#"VERSION "1.0"
599
600BU_: ECM
601
602BO_ 256 Test : 8 ECM
603 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
604"#;
605
606        // Default (strict) mode should fail
607        let result = Dbc::parse(data);
608        assert!(result.is_err());
609    }
610
611    #[cfg(feature = "std")]
612    #[test]
613    fn test_parse_val_value_descriptions() {
614        let data = r#"VERSION ""
615
616NS_ :
617
618BS_:
619
620BU_: Node1 Node2
621
622BO_ 100 Message1 : 8 Node1
623 SG_ Signal : 32|8@1- (1,0) [-1|4] "Gear" Node2
624
625VAL_ 100 Signal -1 "Reverse" 0 "Neutral" 1 "First" 2 "Second" 3 "Third" 4 "Fourth" ;
626"#;
627
628        let dbc = match Dbc::parse(data) {
629            Ok(dbc) => dbc,
630            Err(e) => panic!("Failed to parse DBC: {:?}", e),
631        };
632
633        // Verify basic structure
634        assert_eq!(dbc.messages().len(), 1);
635        let message = dbc.messages().iter().find(|m| m.id() == 100).unwrap();
636        assert_eq!(message.name(), "Message1");
637        assert_eq!(message.sender(), "Node1");
638
639        // Verify value descriptions
640        let value_descriptions = dbc
641            .value_descriptions_for_signal(100, "Signal")
642            .expect("Value descriptions should exist");
643        assert_eq!(value_descriptions.get(0xFFFFFFFF), Some("Reverse")); // -1 as u64
644        assert_eq!(value_descriptions.get(0), Some("Neutral"));
645        assert_eq!(value_descriptions.get(1), Some("First"));
646        assert_eq!(value_descriptions.get(2), Some("Second"));
647        assert_eq!(value_descriptions.get(3), Some("Third"));
648        assert_eq!(value_descriptions.get(4), Some("Fourth"));
649    }
650
651    #[cfg(feature = "std")]
652    #[test]
653    fn test_parse_val_global_value_descriptions() {
654        // Test global value descriptions (VAL_ -1) that apply to all signals with the same name
655        let data = r#"VERSION "1.0"
656
657NS_ :
658
659    VAL_
660
661BS_:
662
663BU_: ECU DASH
664
665BO_ 256 EngineData: 8 ECU
666 SG_ EngineRPM : 0|16@1+ (0.125,0) [0|8000] "rpm" Vector__XXX
667 SG_ DI_gear : 24|3@1+ (1,0) [0|7] "" Vector__XXX
668
669BO_ 512 DashboardDisplay: 8 DASH
670 SG_ DI_gear : 0|3@1+ (1,0) [0|7] "" Vector__XXX
671 SG_ SpeedDisplay : 8|16@1+ (0.01,0) [0|300] "km/h" Vector__XXX
672
673VAL_ -1 DI_gear 0 "INVALID" 1 "P" 2 "R" 3 "N" 4 "D" 5 "S" 6 "L" 7 "SNA" ;
674"#;
675
676        let dbc = match Dbc::parse(data) {
677            Ok(dbc) => dbc,
678            Err(e) => panic!("Failed to parse DBC: {:?}", e),
679        };
680
681        // Verify basic structure
682        assert_eq!(dbc.messages().len(), 2);
683
684        // Verify first message (EngineData)
685        let engine_msg = dbc.messages().iter().find(|m| m.id() == 256).unwrap();
686        assert_eq!(engine_msg.name(), "EngineData");
687        assert_eq!(engine_msg.sender(), "ECU");
688        let di_gear_signal1 = engine_msg.signals().find("DI_gear").unwrap();
689        assert_eq!(di_gear_signal1.name(), "DI_gear");
690        assert_eq!(di_gear_signal1.start_bit(), 24);
691
692        // Verify second message (DashboardDisplay)
693        let dash_msg = dbc.messages().iter().find(|m| m.id() == 512).unwrap();
694        assert_eq!(dash_msg.name(), "DashboardDisplay");
695        assert_eq!(dash_msg.sender(), "DASH");
696        let di_gear_signal2 = dash_msg.signals().find("DI_gear").unwrap();
697        assert_eq!(di_gear_signal2.name(), "DI_gear");
698        assert_eq!(di_gear_signal2.start_bit(), 0);
699
700        // Verify global value descriptions apply to DI_gear in message 256
701        let value_descriptions1 = dbc
702            .value_descriptions_for_signal(256, "DI_gear")
703            .expect("Global value descriptions should exist for DI_gear in message 256");
704
705        assert_eq!(value_descriptions1.get(0), Some("INVALID"));
706        assert_eq!(value_descriptions1.get(1), Some("P"));
707        assert_eq!(value_descriptions1.get(2), Some("R"));
708        assert_eq!(value_descriptions1.get(3), Some("N"));
709        assert_eq!(value_descriptions1.get(4), Some("D"));
710        assert_eq!(value_descriptions1.get(5), Some("S"));
711        assert_eq!(value_descriptions1.get(6), Some("L"));
712        assert_eq!(value_descriptions1.get(7), Some("SNA"));
713
714        // Verify global value descriptions also apply to DI_gear in message 512
715        let value_descriptions2 = dbc
716            .value_descriptions_for_signal(512, "DI_gear")
717            .expect("Global value descriptions should exist for DI_gear in message 512");
718
719        // Both should return the same value descriptions (same reference or same content)
720        assert_eq!(value_descriptions2.get(0), Some("INVALID"));
721        assert_eq!(value_descriptions2.get(1), Some("P"));
722        assert_eq!(value_descriptions2.get(2), Some("R"));
723        assert_eq!(value_descriptions2.get(3), Some("N"));
724        assert_eq!(value_descriptions2.get(4), Some("D"));
725        assert_eq!(value_descriptions2.get(5), Some("S"));
726        assert_eq!(value_descriptions2.get(6), Some("L"));
727        assert_eq!(value_descriptions2.get(7), Some("SNA"));
728
729        // Verify they should be the same instance (both reference the global entry)
730        // Since we store by (Option<u32>, &str), both should return the same entry
731        assert_eq!(value_descriptions1.len(), value_descriptions2.len());
732        assert_eq!(value_descriptions1.len(), 8);
733
734        // Verify other signals don't have value descriptions
735        assert_eq!(dbc.value_descriptions_for_signal(256, "EngineRPM"), None);
736        assert_eq!(dbc.value_descriptions_for_signal(512, "SpeedDisplay"), None);
737    }
738}