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 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 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 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 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 let pos_at_keyword = parser.pos();
75
76 match keyword {
77 NS_ => {
78 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 let _ = parser.expect(keyword.as_bytes()).ok();
114 parser.skip_to_end_of_line();
115 continue;
116 }
117 SG_MUL_VAL_ => {
118 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 if let Some(ext_mux) = ExtendedMultiplexing::parse(&mut parser) {
126 if extended_multiplexing_buffer.push(ext_mux).is_err() {
127 return Err(Error::Validation(Error::EXTENDED_MULTIPLEXING_TOO_MANY));
129 }
130 } else {
131 parser.skip_to_end_of_line();
133 }
134 continue;
135 }
136 VAL_ => {
137 let _ = parser.expect(crate::VAL_.as_bytes()).ok();
139 parser.skip_newlines_and_spaces();
143 let message_id = match parser.parse_i64() {
144 Ok(id) => {
145 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 let mut entries: ValueDescEntries = ValueDescEntries::new();
176 loop {
177 parser.skip_newlines_and_spaces();
178 if parser.starts_with(b";") {
180 parser.expect(b";").ok();
181 break;
182 }
183 let value = match parser.parse_i64() {
187 Ok(v) => {
188 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 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 = Some(Version::parse(&mut parser)?);
229 continue;
230 }
231 BU_ => {
232 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 if message_count_actual >= MAX_MESSAGES {
242 return Err(parser.err_nodes(Error::NODES_TOO_MANY));
243 }
244
245 let message_start_pos = pos_at_keyword;
247
248 let header_line_end = {
251 let mut temp_parser = Parser::new(&data.as_bytes()[pos_at_keyword..])?;
253 temp_parser.expect(crate::BO_.as_bytes()).ok();
255 temp_parser.skip_whitespace().ok();
256 temp_parser.parse_u32().ok(); temp_parser.skip_whitespace().ok();
258 temp_parser.parse_identifier().ok(); temp_parser.skip_whitespace().ok();
260 temp_parser.expect(b":").ok(); temp_parser.skip_whitespace().ok();
262 temp_parser.parse_u8().ok(); temp_parser.skip_whitespace().ok();
264 temp_parser.parse_identifier().ok(); pos_at_keyword + temp_parser.pos()
266 };
267
268 parser.skip_to_end_of_line(); let mut signals_array: Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }> = Vec::new();
272
273 loop {
275 parser.skip_newlines_and_spaces();
276
277 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, };
285
286 if keyword != SG_ {
288 break; }
290
291 if signals_array.len() >= MAX_SIGNALS_PER_MESSAGE {
293 return Err(parser.err_message(Error::MESSAGE_TOO_MANY_SIGNALS));
294 }
295
296 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 if parser.at_newline() {
305 parser.skip_to_end_of_line();
306 }
307 }
308 Err(_) => {
309 parser.skip_to_end_of_line();
311 break;
312 }
313 }
314 }
315
316 let message_input = &data.as_bytes()[message_start_pos..header_line_end];
320 let mut message_parser = Parser::new(message_input)?;
321
322 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 parser.skip_to_end_of_line();
334 continue;
335 }
336 _ => {
337 parser.skip_to_end_of_line();
338 continue;
339 }
340 }
341 }
342
343 let nodes = nodes.unwrap_or_default();
345
346 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 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 let messages_slice: &[Message] = messages_buffer.as_slice();
370 let extended_multiplexing_slice: &[ExtendedMultiplexing] =
371 extended_multiplexing_buffer.as_slice();
372
373 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 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 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 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 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 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 let result = Dbc::parse("");
546 assert!(result.is_err());
547 match result.unwrap_err() {
548 Error::UnexpectedEof { .. } => {
549 }
551 _ => panic!("Expected Error::UnexpectedEof"),
552 }
553 }
554
555 #[test]
556 fn test_parse_bytes_invalid_utf8() {
557 use crate::Error;
558 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 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 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 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 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 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")); 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 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 assert_eq!(dbc.messages().len(), 2);
671
672 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 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 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 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 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 assert_eq!(value_descriptions1.len(), value_descriptions2.len());
720 assert_eq!(value_descriptions1.len(), 8);
721
722 assert_eq!(dbc.value_descriptions_for_signal(256, "EngineRPM"), None);
724 assert_eq!(dbc.value_descriptions_for_signal(512, "SpeedDisplay"), None);
725 }
726
727 #[test]
736 fn test_spec_section_8_3_dlc_zero_is_valid() {
737 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 #[test]
754 fn test_spec_section_8_1_extended_can_id_format() {
755 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 assert_eq!(msg.id() & 0x80000000, 0x80000000); assert_eq!(msg.id() & 0x1FFFFFFF, 0x494); }
771
772 #[test]
774 fn test_spec_section_8_1_max_extended_id() {
775 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 #[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 #[test]
808 fn test_spec_section_9_5_receivers_comma_separated() {
809 use crate::{Parser, Signal};
813
814 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 #[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 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 assert!(mux_switch.is_multiplexer_switch());
849 assert_eq!(mux_switch.multiplexer_switch_value(), None);
850
851 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 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 assert!(err.line().is_some(), "Error should include line number");
874 }
875}