1#[cfg(feature = "alloc")]
2use crate::{Error, Result, error::messages as error_messages};
3use crate::{
4 Message, Messages, Nodes, ParseOptions, Parser, Signal, Signals, Version,
5 error::{ParseError, ParseResult},
6};
7
8#[cfg(feature = "alloc")]
9use alloc::string::String;
10
11#[derive(Debug)]
36pub struct Dbc<'a> {
37 version: Option<Version<'a>>,
38 nodes: Nodes<'a>,
39 messages: Messages<'a>,
40}
41
42impl<'a> Dbc<'a> {
43 pub(crate) fn validate(
44 _version: Option<&Version<'_>>,
45 nodes: &Nodes<'_>,
46 messages: &[Option<Message<'_>>],
47 message_count: usize,
48 ) -> ParseResult<()> {
49 #[cfg(feature = "alloc")]
50 use crate::error::messages as error_messages;
51
52 let messages_slice = &messages[..message_count];
54 for (i, msg1_opt) in messages_slice.iter().enumerate() {
55 let msg1 = match msg1_opt {
56 Some(m) => m,
57 None => continue, };
59 for msg2_opt in messages_slice.iter().skip(i + 1) {
60 let msg2 = match msg2_opt {
61 Some(m) => m,
62 None => continue, };
64 if msg1.id() == msg2.id() {
65 #[cfg(feature = "alloc")]
66 {
67 let msg = error_messages::duplicate_message_id(
68 msg1.id(),
69 msg1.name(),
70 msg2.name(),
71 );
72 return Err(ParseError::Version(msg.leak()));
73 }
74 #[cfg(not(feature = "alloc"))]
75 {
76 return Err(ParseError::Version(
77 crate::error::lang::FORMAT_DUPLICATE_MESSAGE_ID,
78 ));
79 }
80 }
81 }
82 }
83
84 if !nodes.is_empty() {
87 for msg_opt in messages_slice {
88 let msg = match msg_opt {
89 Some(m) => m,
90 None => continue, };
92 if !nodes.contains(msg.sender()) {
93 #[cfg(feature = "alloc")]
94 {
95 let msg_str = error_messages::sender_not_in_nodes(msg.name(), msg.sender());
96 return Err(ParseError::Version(msg_str.leak()));
97 }
98 #[cfg(not(feature = "alloc"))]
99 {
100 return Err(ParseError::Version(
101 crate::error::lang::FORMAT_SENDER_NOT_IN_NODES,
102 ));
103 }
104 }
105 }
106 }
107
108 Ok(())
109 }
110
111 #[allow(dead_code)] pub(crate) fn new(
113 version: Option<Version<'a>>,
114 nodes: Nodes<'a>,
115 messages: &'a [Message<'a>],
116 ) -> Self {
117 Self {
119 version,
120 nodes,
121 messages: Messages::from_messages_slice(messages),
122 }
123 }
124
125 fn new_from_options(
126 version: Option<Version<'a>>,
127 nodes: Nodes<'a>,
128 messages: &[Option<Message<'a>>],
129 message_count: usize,
130 ) -> Self {
131 Self {
133 version,
134 nodes,
135 messages: Messages::from_options_slice(messages, message_count),
136 }
137 }
138
139 #[inline]
154 #[must_use]
155 pub fn version(&self) -> Option<&Version<'a>> {
156 self.version.as_ref()
157 }
158
159 #[inline]
177 #[must_use]
178 pub fn nodes(&self) -> &Nodes<'a> {
179 &self.nodes
180 }
181
182 #[inline]
198 #[must_use]
199 pub fn messages(&self) -> &Messages<'a> {
200 &self.messages
201 }
202
203 pub fn parse(data: &'a str) -> ParseResult<Self> {
222 Self::parse_with_options(data, ParseOptions::default())
223 }
224
225 pub fn parse_with_options(data: &'a str, options: ParseOptions) -> ParseResult<Self> {
251 let mut parser1 = Parser::new(data.as_bytes())?;
253 let _ = Messages::count_messages_and_signals(&mut parser1)?;
254
255 let mut parser2 = Parser::new(data.as_bytes())?;
257
258 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
262 let mut messages_buffer = Messages::new_parse_buffer();
263
264 #[cfg(any(feature = "alloc", feature = "kernel"))]
265 let mut messages_buffer: alloc::vec::Vec<Option<Message<'a>>> = {
266 use crate::compat::vec_with_capacity;
267 vec_with_capacity(Messages::max_capacity())
268 };
269
270 let mut message_count_actual = 0;
271
272 use crate::{
274 BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SIG_GROUP_,
275 SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
276 };
277
278 let mut version: Option<Version<'a>> = None;
279 let mut nodes: Option<Nodes<'a>> = None;
280
281 loop {
282 parser2.skip_newlines_and_spaces();
284 if parser2.starts_with(b"//") {
285 parser2.skip_to_end_of_line();
286 continue;
287 }
288
289 let keyword_result = parser2.find_next_keyword();
290 let keyword = match keyword_result {
291 Ok(kw) => kw,
292 Err(ParseError::UnexpectedEof) => break,
293 Err(ParseError::Expected(_)) => {
294 if parser2.starts_with(b"//") {
295 parser2.skip_to_end_of_line();
296 continue;
297 }
298 return Err(keyword_result.unwrap_err());
299 }
300 Err(e) => return Err(e),
301 };
302
303 match keyword {
304 NS_ => {
305 parser2.skip_newlines_and_spaces();
306 let _ = parser2.expect(b":").ok();
307 loop {
308 parser2.skip_newlines_and_spaces();
309 if parser2.is_empty() {
310 break;
311 }
312 if parser2.starts_with(b" ") || parser2.starts_with(b"\t") {
313 parser2.skip_to_end_of_line();
314 continue;
315 }
316 if parser2.starts_with(b"//") {
317 parser2.skip_to_end_of_line();
318 continue;
319 }
320 if parser2.starts_with(BS_.as_bytes())
321 || parser2.starts_with(BU_.as_bytes())
322 || parser2.starts_with(BO_.as_bytes())
323 || parser2.starts_with(SG_.as_bytes())
324 || parser2.starts_with(VERSION.as_bytes())
325 {
326 break;
327 }
328 parser2.skip_to_end_of_line();
329 }
330 continue;
331 }
332 CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | VAL_ | SIG_GROUP_
333 | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
334 parser2.skip_to_end_of_line();
335 continue;
336 }
337 VERSION => {
338 version = Some(Version::parse(&mut parser2)?);
339 continue;
340 }
341 BU_ => {
342 nodes = Some(Nodes::parse(&mut parser2)?);
343 continue;
344 }
345 BO_ => {
346 if message_count_actual >= Messages::max_capacity() {
348 return Err(ParseError::Version(crate::error::lang::NODES_TOO_MANY));
349 }
350
351 let message_start_pos = parser2.pos();
353
354 parser2.skip_newlines_and_spaces();
356 let _id = parser2.parse_u32().ok();
357 parser2.skip_newlines_and_spaces();
358 let _name = parser2.parse_identifier().ok();
359 parser2.skip_newlines_and_spaces();
360 let _ = parser2.expect(b":").ok();
361 parser2.skip_newlines_and_spaces();
362 let _dlc = parser2.parse_u8().ok();
363 parser2.skip_newlines_and_spaces();
364 let _sender = parser2.parse_identifier().ok();
365 let message_header_end_pos = parser2.pos();
366 parser2.skip_to_end_of_line();
367
368 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
370 let mut signals_array = Signals::new_parse_buffer();
371
372 #[cfg(any(feature = "alloc", feature = "kernel"))]
373 let mut signals_array: alloc::vec::Vec<Option<Signal<'a>>> = {
374 use crate::compat::vec_with_capacity;
375 vec_with_capacity(Signals::max_capacity())
376 };
377
378 let mut signal_count = 0;
379 loop {
380 parser2.skip_newlines_and_spaces();
381 if parser2.starts_with(crate::SG_.as_bytes()) {
382 if let Some(next_byte) = parser2.peek_byte_at(3) {
383 if matches!(next_byte, b' ' | b'\n' | b'\r' | b'\t') {
384 if signal_count >= Signals::max_capacity() {
385 return Err(ParseError::Version(
386 crate::error::messages::SIGNAL_RECEIVERS_TOO_MANY,
387 ));
388 }
389 let _kw = parser2.find_next_keyword().map_err(|e| match e {
390 ParseError::Expected(_) => {
391 ParseError::Expected("Expected SG_ keyword")
392 }
393 _ => e,
394 })?;
395 let signal = Signal::parse(&mut parser2)?;
396 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
397 {
398 signals_array[signal_count] = Some(signal);
399 }
400 #[cfg(any(feature = "alloc", feature = "kernel"))]
401 {
402 signals_array.push(Some(signal));
403 }
404 signal_count += 1;
405 continue;
406 }
407 }
408 }
409 break;
410 }
411
412 let message_input = &data.as_bytes()[message_start_pos..message_header_end_pos];
416 let mut message_parser = Parser::new(message_input)?;
417
418 let signals_slice: &[Option<Signal<'a>>] = {
420 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
421 {
422 &signals_array[..signal_count]
423 }
424 #[cfg(any(feature = "alloc", feature = "kernel"))]
425 {
426 &signals_array[..]
427 }
428 };
429 let message =
430 Message::parse(&mut message_parser, signals_slice, signal_count, options)?;
431
432 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
433 {
434 messages_buffer[message_count_actual] = Some(message);
435 }
436 #[cfg(any(feature = "alloc", feature = "kernel"))]
437 {
438 messages_buffer.push(Some(message));
439 }
440 message_count_actual += 1;
441 continue;
442 }
443 SG_ => {
444 let _ = Signal::parse(&mut parser2)?;
445 continue;
446 }
447 _ => {
448 parser2.skip_to_end_of_line();
449 continue;
450 }
451 }
452 }
453
454 let nodes = nodes.unwrap_or_default();
456
457 let version = version.or_else(|| {
459 static EMPTY_VERSION: &[u8] = b"VERSION \"\"";
460 let mut parser = Parser::new(EMPTY_VERSION).ok()?;
461 Version::parse(&mut parser).ok()
462 });
463
464 let messages_slice: &[Option<Message<'a>>] = {
466 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
467 {
468 &messages_buffer[..message_count_actual]
469 }
470 #[cfg(any(feature = "alloc", feature = "kernel"))]
471 {
472 &messages_buffer[..]
473 }
474 };
475
476 Self::validate(
478 version.as_ref(),
479 &nodes,
480 messages_slice,
481 message_count_actual,
482 )?;
483
484 Ok(Self::new_from_options(
486 version,
487 nodes,
488 messages_slice,
489 message_count_actual,
490 ))
491 }
492
493 #[cfg(feature = "alloc")]
506 pub fn parse_bytes(data: &[u8]) -> Result<Dbc<'static>> {
507 let content =
508 core::str::from_utf8(data).map_err(|e| Error::Dbc(error_messages::invalid_utf8(e)))?;
509 use alloc::boxed::Box;
511 let owned = String::from(content);
512 let boxed = owned.into_boxed_str();
513 let content_ref: &'static str = Box::leak(boxed);
514 Dbc::parse(content_ref).map_err(Error::ParseError)
515 }
516
517 #[cfg(feature = "std")]
539 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Dbc<'static>> {
540 let file =
541 std::fs::File::open(path).map_err(|e| Error::Dbc(error_messages::read_failed(e)))?;
542 Self::from_reader(file)
543 }
544
545 #[cfg(feature = "std")]
560 pub fn from_reader<R: std::io::Read>(mut reader: R) -> Result<Dbc<'static>> {
561 let mut buffer = String::new();
562 std::io::Read::read_to_string(&mut reader, &mut buffer)
563 .map_err(|e| Error::Dbc(error_messages::read_failed(e)))?;
564 use alloc::boxed::Box;
567 let boxed = buffer.into_boxed_str();
568 let content_ref: &'static str = Box::leak(boxed);
569 Dbc::parse(content_ref).map_err(Error::ParseError)
570 }
571
572 #[cfg(feature = "alloc")]
586 #[must_use]
587 pub fn to_dbc_string(&self) -> alloc::string::String {
588 use alloc::string::String;
589 let signal_count: usize = self.messages().iter().map(|m| m.signals().len()).sum();
592 let estimated_capacity = 200 + (self.messages.len() * 50) + (signal_count * 100);
593 let mut result = String::with_capacity(estimated_capacity);
594
595 if let Some(version) = &self.version {
597 result.push_str(&version.to_dbc_string());
598 result.push_str("\n\n");
599 }
600
601 result.push_str(&self.nodes.to_dbc_string());
603 result.push('\n');
604
605 for message in self.messages().iter() {
607 result.push('\n');
608 result.push_str(&message.to_dbc_string_with_signals());
609 }
610
611 result
612 }
613}
614
615#[cfg(feature = "alloc")]
616impl<'a> core::fmt::Display for Dbc<'a> {
617 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
618 write!(f, "{}", self.to_dbc_string())
619 }
620}
621
622#[cfg(all(test, feature = "std"))]
623mod tests {
624 #![allow(clippy::float_cmp)]
625 use super::*;
626 use crate::{
627 Error,
628 error::{ParseError, lang},
629 };
630
631 #[test]
632 fn parses_real_dbc() {
633 let data = r#"VERSION "1.0"
634
635BU_: ECM TCM
636
637BO_ 256 Engine : 8 ECM
638 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
639 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
640
641BO_ 512 Brake : 4 TCM
642 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
643
644 let dbc = Dbc::parse(data).unwrap();
645 assert_eq!(dbc.messages().len(), 2);
646 let mut messages_iter = dbc.messages().iter();
647 let msg0 = messages_iter.next().unwrap();
648 assert_eq!(msg0.signals().len(), 2);
649 let mut signals_iter = msg0.signals().iter();
650 assert_eq!(signals_iter.next().unwrap().name(), "RPM");
651 assert_eq!(signals_iter.next().unwrap().name(), "Temp");
652 let msg1 = messages_iter.next().unwrap();
653 assert_eq!(msg1.signals().len(), 1);
654 assert_eq!(msg1.signals().iter().next().unwrap().name(), "Pressure");
655 }
656
657 #[test]
658 fn test_parse_duplicate_message_id() {
659 let data = r#"VERSION "1.0"
661
662BU_: ECM
663
664BO_ 256 EngineData1 : 8 ECM
665 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
666
667BO_ 256 EngineData2 : 8 ECM
668 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
669"#;
670
671 let result = Dbc::parse(data);
672 assert!(result.is_err());
673 match result.unwrap_err() {
674 ParseError::Version(msg) => {
675 let template_text = lang::FORMAT_DUPLICATE_MESSAGE_ID.split("{}").next().unwrap();
677 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
678 }
679 _ => panic!("Expected ParseError::Version"),
680 }
681 }
682
683 #[test]
684 fn test_parse_sender_not_in_nodes() {
685 let data = r#"VERSION "1.0"
687
688BU_: ECM
689
690BO_ 256 EngineData : 8 TCM
691 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
692"#;
693
694 let result = Dbc::parse(data);
695 assert!(result.is_err());
696 match result.unwrap_err() {
697 ParseError::Version(msg) => {
698 let template_text = lang::FORMAT_SENDER_NOT_IN_NODES.split("{}").next().unwrap();
700 assert!(msg.contains(template_text.trim_end()));
701 }
702 _ => panic!("Expected ParseError::Version"),
703 }
704 }
705
706 #[test]
707 fn test_parse_empty_file() {
708 let result = Dbc::parse("");
710 assert!(result.is_err());
711 match result.unwrap_err() {
712 ParseError::UnexpectedEof => {
713 }
715 _ => panic!("Expected ParseError::UnexpectedEof"),
716 }
717 }
718
719 #[test]
720 fn test_parse_missing_nodes() {
721 let data = r#"VERSION "1.0"
725
726BO_ 256 EngineData : 8 ECM
727 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
728"#;
729
730 let result = Dbc::parse(data);
731 assert!(result.is_ok());
733 let dbc = result.unwrap();
734 assert!(dbc.nodes().is_empty());
735 }
736
737 #[test]
738 fn test_parse_bytes() {
739 let data = r#"VERSION "1.0"
740
741BU_: ECM
742
743BO_ 256 Engine : 8 ECM
744 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
745"#;
746
747 let bytes = data.as_bytes();
748 let dbc = Dbc::parse_bytes(bytes).unwrap();
749 assert_eq!(
750 dbc.version().map(|v| v.to_string()),
751 Some("1.0".to_string())
752 );
753 assert_eq!(dbc.messages().len(), 1);
754 }
755
756 #[test]
757 #[cfg(feature = "alloc")]
758 fn test_parse_from_string() {
759 let data = String::from(
760 r#"VERSION "1.0"
761
762BU_: ECM
763
764BO_ 256 Engine : 8 ECM
765 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
766"#,
767 );
768
769 let dbc = Dbc::parse(&data).unwrap();
770 assert_eq!(
771 dbc.version().map(|v| v.to_string()),
772 Some("1.0".to_string())
773 );
774 assert_eq!(dbc.messages().len(), 1);
775 }
776
777 #[test]
778 fn test_parse_bytes_invalid_utf8() {
779 let invalid_bytes = &[0xFF, 0xFE, 0xFD];
781 let result = Dbc::parse_bytes(invalid_bytes);
782 assert!(result.is_err());
783 match result.unwrap_err() {
784 Error::Dbc(msg) => {
785 let template_text = lang::FORMAT_INVALID_UTF8.split("{}").next().unwrap();
787 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
788 }
789 _ => panic!("Expected Dbc error"),
790 }
791 }
792
793 #[test]
794 #[cfg(feature = "alloc")]
795 fn test_save_basic() {
796 let dbc_content = r#"VERSION "1.0"
798
799BU_: ECM
800
801BO_ 256 EngineData : 8 ECM
802 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
803"#;
804 let dbc = Dbc::parse(dbc_content).unwrap();
805
806 let saved = dbc.to_dbc_string();
807 assert!(saved.contains("VERSION \"1.0\""));
808 assert!(saved.contains("BU_: ECM"));
809 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
810 assert!(saved.contains("SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *")); }
812
813 #[test]
814 fn test_save_round_trip() {
815 let original = r#"VERSION "1.0"
816
817BU_: ECM TCM
818
819BO_ 256 EngineData : 8 ECM
820 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
821 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
822
823BO_ 512 BrakeData : 4 TCM
824 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
825"#;
826
827 let dbc = Dbc::parse(original).unwrap();
828 let saved = dbc.to_dbc_string();
829 let dbc2 = Dbc::parse(&saved).unwrap();
830
831 assert_eq!(
833 dbc.version().map(|v| v.to_string()),
834 dbc2.version().map(|v| v.to_string())
835 );
836 assert_eq!(dbc.messages().len(), dbc2.messages().len());
837
838 for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
839 assert_eq!(msg1.id(), msg2.id());
840 assert_eq!(msg1.name(), msg2.name());
841 assert_eq!(msg1.dlc(), msg2.dlc());
842 assert_eq!(msg1.sender(), msg2.sender());
843 assert_eq!(msg1.signals().len(), msg2.signals().len());
844
845 for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
846 assert_eq!(sig1.name(), sig2.name());
847 assert_eq!(sig1.start_bit(), sig2.start_bit());
848 assert_eq!(sig1.length(), sig2.length());
849 assert_eq!(sig1.byte_order(), sig2.byte_order());
850 assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
851 assert_eq!(sig1.factor(), sig2.factor());
852 assert_eq!(sig1.offset(), sig2.offset());
853 assert_eq!(sig1.min(), sig2.min());
854 assert_eq!(sig1.max(), sig2.max());
855 assert_eq!(sig1.unit(), sig2.unit());
856 assert_eq!(sig1.receivers(), sig2.receivers());
857 }
858 }
859 }
860
861 #[test]
862 #[cfg(feature = "alloc")]
863 fn test_save_multiple_messages() {
864 let dbc_content = r#"VERSION "1.0"
866
867BU_: ECM TCM
868
869BO_ 256 EngineData : 8 ECM
870 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
871
872BO_ 512 BrakeData : 4 TCM
873 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar"
874"#;
875 let dbc = Dbc::parse(dbc_content).unwrap();
876 let saved = dbc.to_dbc_string();
877
878 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
880 assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
881 assert!(saved.contains("SG_ RPM"));
882 assert!(saved.contains("SG_ Pressure"));
883 }
884
885 #[test]
889 fn test_parse_without_version() {
890 let data = r#"
892BU_: ECM
893
894BO_ 256 Engine : 8 ECM
895 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
896"#;
897 let dbc = Dbc::parse(data).unwrap();
898 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
899 }
900
901 #[test]
902 fn test_parse_without_version_with_comment() {
903 let data = r#"// This is a comment
905BU_: ECM
906
907BO_ 256 Engine : 8 ECM
908 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
909"#;
910 let dbc = Dbc::parse(data).unwrap();
911 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
912 }
913
914 #[test]
915 fn test_parse_error_with_line_number() {
916 let data = r#"VERSION "1.0"
918
919BU_: ECM
920
921BO_ 256 Engine : 8 ECM
922 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
923BO_ 257 Invalid : 8 ECM
924 SG_ InvalidSignal : invalid|16@1+ (0.25,0) [0|8000] "rpm"
925"#;
926 let result = Dbc::parse(data);
927 assert!(result.is_err());
928 let err = result.unwrap_err();
929 match err {
931 ParseError::Version(_)
932 | ParseError::UnexpectedEof
933 | ParseError::Expected(_)
934 | ParseError::InvalidChar(_) => {
935 }
937 _ => panic!("Expected ParseError"),
938 };
939 }
941
942 #[test]
943 fn test_parse_error_version_with_line_number() {
944 let data = r#"VERSION invalid
946
947BU_: ECM
948"#;
949 let result = Dbc::parse(data);
950 assert!(result.is_err());
951 let err = result.unwrap_err();
952 match err {
954 ParseError::Version(_) | ParseError::UnexpectedEof | ParseError::Expected(_) => {
955 }
957 _ => panic!("Expected ParseError"),
958 };
959 }
961
962 #[test]
963 fn test_parse_with_lenient_boundary_check() {
964 let data = r#"VERSION "1.0"
966
967BU_: ECM
968
969BO_ 256 Test : 8 ECM
970 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
971"#;
972
973 let result = Dbc::parse(data);
975 assert!(result.is_err());
976
977 let options = ParseOptions::lenient();
979 let dbc = Dbc::parse_with_options(data, options).unwrap();
980 assert_eq!(dbc.messages().len(), 1);
981 let message = dbc.messages().at(0).unwrap();
982 assert_eq!(message.signals().len(), 1);
983 assert_eq!(message.signals().at(0).unwrap().name(), "CHECKSUM");
984 }
985
986 #[test]
987 fn test_parse_with_strict_boundary_check() {
988 let data = r#"VERSION "1.0"
990
991BU_: ECM
992
993BO_ 256 Test : 8 ECM
994 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
995"#;
996
997 let result = Dbc::parse(data);
999 assert!(result.is_err());
1000
1001 let options = ParseOptions::new();
1003 let result = Dbc::parse_with_options(data, options);
1004 assert!(result.is_err());
1005 }
1006}