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