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]
153 #[must_use]
154 pub fn version(&self) -> Option<&Version<'a>> {
155 self.version.as_ref()
156 }
157
158 #[inline]
172 #[must_use]
173 pub fn nodes(&self) -> &Nodes<'a> {
174 &self.nodes
175 }
176
177 #[inline]
193 #[must_use]
194 pub fn messages(&self) -> &Messages<'a> {
195 &self.messages
196 }
197
198 pub fn parse(data: &'a str) -> ParseResult<Self> {
217 Self::parse_with_options(data, ParseOptions::default())
218 }
219
220 pub fn parse_with_options(data: &'a str, options: ParseOptions) -> ParseResult<Self> {
246 let mut parser1 = Parser::new(data.as_bytes())?;
248 let _ = Messages::count_messages_and_signals(&mut parser1)?;
249
250 let mut parser2 = Parser::new(data.as_bytes())?;
252
253 #[cfg(not(feature = "alloc"))]
257 let mut messages_buffer = Messages::new_parse_buffer();
258
259 #[cfg(feature = "alloc")]
260 let mut messages_buffer: alloc::vec::Vec<Option<Message<'a>>> =
261 alloc::vec::Vec::with_capacity(Messages::max_capacity());
262
263 let mut message_count_actual = 0;
264
265 use crate::{
267 BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SIG_GROUP_,
268 SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
269 };
270
271 let mut version: Option<Version<'a>> = None;
272 let mut nodes: Option<Nodes<'a>> = None;
273
274 loop {
275 parser2.skip_newlines_and_spaces();
277 if parser2.starts_with(b"//") {
278 parser2.skip_to_end_of_line();
279 continue;
280 }
281
282 let keyword_result = parser2.find_next_keyword();
283 let keyword = match keyword_result {
284 Ok(kw) => kw,
285 Err(ParseError::UnexpectedEof) => break,
286 Err(ParseError::Expected(_)) => {
287 if parser2.starts_with(b"//") {
288 parser2.skip_to_end_of_line();
289 continue;
290 }
291 return Err(keyword_result.unwrap_err());
292 }
293 Err(e) => return Err(e),
294 };
295
296 match keyword {
297 NS_ => {
298 parser2.skip_newlines_and_spaces();
299 let _ = parser2.expect(b":").ok();
300 loop {
301 parser2.skip_newlines_and_spaces();
302 if parser2.is_empty() {
303 break;
304 }
305 if parser2.starts_with(b" ") || parser2.starts_with(b"\t") {
306 parser2.skip_to_end_of_line();
307 continue;
308 }
309 if parser2.starts_with(b"//") {
310 parser2.skip_to_end_of_line();
311 continue;
312 }
313 if parser2.starts_with(BS_.as_bytes())
314 || parser2.starts_with(BU_.as_bytes())
315 || parser2.starts_with(BO_.as_bytes())
316 || parser2.starts_with(SG_.as_bytes())
317 || parser2.starts_with(VERSION.as_bytes())
318 {
319 break;
320 }
321 parser2.skip_to_end_of_line();
322 }
323 continue;
324 }
325 CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | VAL_ | SIG_GROUP_
326 | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
327 parser2.skip_to_end_of_line();
328 continue;
329 }
330 VERSION => {
331 version = Some(Version::parse(&mut parser2)?);
332 continue;
333 }
334 BU_ => {
335 nodes = Some(Nodes::parse(&mut parser2)?);
336 continue;
337 }
338 BO_ => {
339 if message_count_actual >= Messages::max_capacity() {
341 return Err(ParseError::Version(crate::error::lang::NODES_TOO_MANY));
342 }
343
344 let message_start_pos = parser2.pos();
346
347 parser2.skip_newlines_and_spaces();
349 let _id = parser2.parse_u32().ok();
350 parser2.skip_newlines_and_spaces();
351 let _name = parser2.parse_identifier().ok();
352 parser2.skip_newlines_and_spaces();
353 let _ = parser2.expect(b":").ok();
354 parser2.skip_newlines_and_spaces();
355 let _dlc = parser2.parse_u8().ok();
356 parser2.skip_newlines_and_spaces();
357 let _sender = parser2.parse_identifier().ok();
358 let message_header_end_pos = parser2.pos();
359 parser2.skip_to_end_of_line();
360
361 #[cfg(not(feature = "alloc"))]
363 let mut signals_array = Signals::new_parse_buffer();
364
365 #[cfg(feature = "alloc")]
366 let mut signals_array: alloc::vec::Vec<Option<Signal<'a>>> =
367 alloc::vec::Vec::with_capacity(Signals::max_capacity());
368
369 let mut signal_count = 0;
370 loop {
371 parser2.skip_newlines_and_spaces();
372 if parser2.starts_with(crate::SG_.as_bytes()) {
373 if let Some(next_byte) = parser2.peek_byte_at(3) {
374 if matches!(next_byte, b' ' | b'\n' | b'\r' | b'\t') {
375 if signal_count >= Signals::max_capacity() {
376 return Err(ParseError::Version(
377 crate::error::messages::SIGNAL_RECEIVERS_TOO_MANY,
378 ));
379 }
380 let _kw = parser2.find_next_keyword().map_err(|e| match e {
381 ParseError::Expected(_) => {
382 ParseError::Expected("Expected SG_ keyword")
383 }
384 _ => e,
385 })?;
386 let signal = Signal::parse(&mut parser2)?;
387 #[cfg(not(feature = "alloc"))]
388 {
389 signals_array[signal_count] = Some(signal);
390 }
391 #[cfg(feature = "alloc")]
392 {
393 signals_array.push(Some(signal));
394 }
395 signal_count += 1;
396 continue;
397 }
398 }
399 }
400 break;
401 }
402
403 let message_input = &data.as_bytes()[message_start_pos..message_header_end_pos];
407 let mut message_parser = Parser::new(message_input)?;
408
409 let signals_slice: &[Option<Signal<'a>>] = {
411 #[cfg(not(feature = "alloc"))]
412 {
413 &signals_array[..signal_count]
414 }
415 #[cfg(feature = "alloc")]
416 {
417 &signals_array[..]
418 }
419 };
420 let message =
421 Message::parse(&mut message_parser, signals_slice, signal_count, options)?;
422
423 #[cfg(not(feature = "alloc"))]
424 {
425 messages_buffer[message_count_actual] = Some(message);
426 }
427 #[cfg(feature = "alloc")]
428 {
429 messages_buffer.push(Some(message));
430 }
431 message_count_actual += 1;
432 continue;
433 }
434 SG_ => {
435 let _ = Signal::parse(&mut parser2)?;
436 continue;
437 }
438 _ => {
439 parser2.skip_to_end_of_line();
440 continue;
441 }
442 }
443 }
444
445 let nodes = nodes.unwrap_or_default();
447
448 let version = version.or_else(|| {
450 static EMPTY_VERSION: &[u8] = b"VERSION \"\"";
451 let mut parser = Parser::new(EMPTY_VERSION).ok()?;
452 Version::parse(&mut parser).ok()
453 });
454
455 let messages_slice: &[Option<Message<'a>>] = {
457 #[cfg(not(feature = "alloc"))]
458 {
459 &messages_buffer[..message_count_actual]
460 }
461 #[cfg(feature = "alloc")]
462 {
463 &messages_buffer[..]
464 }
465 };
466
467 Self::validate(
469 version.as_ref(),
470 &nodes,
471 messages_slice,
472 message_count_actual,
473 )?;
474
475 Ok(Self::new_from_options(
477 version,
478 nodes,
479 messages_slice,
480 message_count_actual,
481 ))
482 }
483
484 #[cfg(feature = "alloc")]
497 pub fn parse_bytes(data: &[u8]) -> Result<Dbc<'static>> {
498 let content =
499 core::str::from_utf8(data).map_err(|e| Error::Dbc(error_messages::invalid_utf8(e)))?;
500 let owned = String::from(content);
502 let boxed = owned.into_boxed_str();
503 let content_ref: &'static str = Box::leak(boxed);
504 Dbc::parse(content_ref).map_err(Error::ParseError)
505 }
506
507 #[cfg(feature = "std")]
529 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Dbc<'static>> {
530 let file =
531 std::fs::File::open(path).map_err(|e| Error::Dbc(error_messages::read_failed(e)))?;
532 Self::from_reader(file)
533 }
534
535 #[cfg(feature = "std")]
550 pub fn from_reader<R: std::io::Read>(mut reader: R) -> Result<Dbc<'static>> {
551 let mut buffer = String::new();
552 std::io::Read::read_to_string(&mut reader, &mut buffer)
553 .map_err(|e| Error::Dbc(error_messages::read_failed(e)))?;
554 let boxed = buffer.into_boxed_str();
557 let content_ref: &'static str = Box::leak(boxed);
558 Dbc::parse(content_ref).map_err(Error::ParseError)
559 }
560
561 #[cfg(feature = "alloc")]
575 #[must_use]
576 pub fn to_dbc_string(&self) -> String {
577 let signal_count: usize = self.messages().iter().map(|m| m.signals().len()).sum();
580 let estimated_capacity = 200 + (self.messages.len() * 50) + (signal_count * 100);
581 let mut result = String::with_capacity(estimated_capacity);
582
583 if let Some(version) = &self.version {
585 result.push_str(&version.to_dbc_string());
586 result.push_str("\n\n");
587 }
588
589 result.push_str(&self.nodes.to_dbc_string());
591 result.push('\n');
592
593 for message in self.messages().iter() {
595 result.push('\n');
596 result.push_str(&message.to_dbc_string_with_signals());
597 }
598
599 result
600 }
601}
602
603#[cfg(feature = "alloc")]
604impl<'a> core::fmt::Display for Dbc<'a> {
605 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
606 write!(f, "{}", self.to_dbc_string())
607 }
608}
609
610#[cfg(all(test, feature = "std"))]
611mod tests {
612 #![allow(clippy::float_cmp)]
613 use super::*;
614 use crate::{
615 ByteOrder, Error, Parser, Receivers, Version,
616 error::{ParseError, lang},
617 nodes::NodesBuilder,
618 };
619 use crate::{DbcBuilder, MessageBuilder, ReceiversBuilder, SignalBuilder};
620
621 #[test]
622 fn test_dbc_new_valid() {
623 let mut parser = Parser::new(b"VERSION \"1.0\"").unwrap();
624 let version = Version::parse(&mut parser).unwrap();
625 let nodes = NodesBuilder::new().add_node("ECM").add_node("TCM").build().unwrap();
626
627 let signal1 = SignalBuilder::new()
628 .name("RPM")
629 .start_bit(0)
630 .length(16)
631 .byte_order(ByteOrder::BigEndian)
632 .unsigned(true)
633 .factor(0.25)
634 .offset(0.0)
635 .min(0.0)
636 .max(8000.0)
637 .unit("rpm")
638 .receivers(Receivers::Broadcast)
639 .build()
640 .unwrap();
641
642 let signal2 = SignalBuilder::new()
643 .name("Temperature")
644 .start_bit(16)
645 .length(8)
646 .byte_order(ByteOrder::BigEndian)
647 .unsigned(true)
648 .factor(1.0)
649 .offset(-40.0)
650 .min(-40.0)
651 .max(215.0)
652 .unit("°C")
653 .receivers(Receivers::Broadcast)
654 .build()
655 .unwrap();
656
657 let message1 = MessageBuilder::new()
658 .id(256)
659 .name("EngineData")
660 .dlc(8)
661 .sender("ECM")
662 .add_signal(signal1)
663 .add_signal(signal2)
664 .build()
665 .unwrap();
666 let message2 = MessageBuilder::new()
667 .id(512)
668 .name("BrakeData")
669 .dlc(4)
670 .sender("TCM")
671 .build()
672 .unwrap();
673
674 let dbc = DbcBuilder::new()
675 .version(version)
676 .nodes(nodes)
677 .add_message(message1)
678 .add_message(message2)
679 .build()
680 .unwrap();
681 assert_eq!(dbc.messages().len(), 2);
682 let mut messages_iter = dbc.messages().iter();
683 assert_eq!(messages_iter.next().unwrap().id(), 256);
684 assert_eq!(messages_iter.next().unwrap().id(), 512);
685 }
686
687 #[test]
688 fn test_dbc_new_duplicate_message_id() {
689 let mut parser = Parser::new(b"VERSION \"1.0\"").unwrap();
690 let version = Version::parse(&mut parser).unwrap();
691 let nodes = NodesBuilder::new().add_node("ECM").build().unwrap();
692
693 let signal = SignalBuilder::new()
694 .name("RPM")
695 .start_bit(0)
696 .length(16)
697 .byte_order(ByteOrder::BigEndian)
698 .unsigned(true)
699 .factor(1.0)
700 .offset(0.0)
701 .min(0.0)
702 .max(100.0)
703 .receivers(Receivers::None)
704 .build()
705 .unwrap();
706
707 let message1 = MessageBuilder::new()
708 .id(256)
709 .name("EngineData1")
710 .dlc(8)
711 .sender("ECM")
712 .add_signal(signal.clone())
713 .build()
714 .unwrap();
715 let message2 = MessageBuilder::new()
716 .id(256)
717 .name("EngineData2")
718 .dlc(8)
719 .sender("ECM")
720 .add_signal(signal)
721 .build()
722 .unwrap();
723
724 let result = DbcBuilder::new()
725 .version(version)
726 .nodes(nodes)
727 .add_message(message1)
728 .add_message(message2)
729 .build();
730 assert!(result.is_err());
731 match result.unwrap_err() {
732 Error::Dbc(msg) => {
733 let template_text = lang::FORMAT_DUPLICATE_MESSAGE_ID.split("{}").next().unwrap();
735 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
736 }
737 _ => panic!("Expected Error::Dbc"),
738 }
739 }
740
741 #[test]
742 fn test_dbc_new_sender_not_in_nodes() {
743 let mut parser = Parser::new(b"VERSION \"1.0\"").unwrap();
744 let version = Version::parse(&mut parser).unwrap();
745 let nodes = NodesBuilder::new().add_node("ECM").build().unwrap(); let signal = SignalBuilder::new()
748 .name("RPM")
749 .start_bit(0)
750 .length(16)
751 .byte_order(ByteOrder::BigEndian)
752 .unsigned(true)
753 .factor(1.0)
754 .offset(0.0)
755 .min(0.0)
756 .max(100.0)
757 .receivers(Receivers::None)
758 .build()
759 .unwrap();
760
761 let message = MessageBuilder::new()
762 .id(256)
763 .name("EngineData")
764 .dlc(8)
765 .sender("TCM")
766 .add_signal(signal)
767 .build()
768 .unwrap();
769
770 let result = DbcBuilder::new().version(version).nodes(nodes).add_message(message).build();
771 assert!(result.is_err());
772 match result.unwrap_err() {
773 Error::Dbc(msg) => {
774 let template_text = lang::FORMAT_SENDER_NOT_IN_NODES.split("{}").next().unwrap();
776 assert!(msg.contains(template_text.trim_end()));
777 }
778 _ => panic!("Expected Error::Dbc"),
779 }
780 }
781
782 #[test]
783 fn parses_real_dbc() {
784 let data = r#"VERSION "1.0"
785
786BU_: ECM TCM
787
788BO_ 256 Engine : 8 ECM
789 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
790 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
791
792BO_ 512 Brake : 4 TCM
793 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
794
795 let dbc = Dbc::parse(data).unwrap();
796 assert_eq!(dbc.messages().len(), 2);
797 let mut messages_iter = dbc.messages().iter();
798 let msg0 = messages_iter.next().unwrap();
799 assert_eq!(msg0.signals().len(), 2);
800 let mut signals_iter = msg0.signals().iter();
801 assert_eq!(signals_iter.next().unwrap().name(), "RPM");
802 assert_eq!(signals_iter.next().unwrap().name(), "Temp");
803 let msg1 = messages_iter.next().unwrap();
804 assert_eq!(msg1.signals().len(), 1);
805 assert_eq!(msg1.signals().iter().next().unwrap().name(), "Pressure");
806 }
807
808 #[test]
809 fn test_parse_duplicate_message_id() {
810 let data = r#"VERSION "1.0"
812
813BU_: ECM
814
815BO_ 256 EngineData1 : 8 ECM
816 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
817
818BO_ 256 EngineData2 : 8 ECM
819 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
820"#;
821
822 let result = Dbc::parse(data);
823 assert!(result.is_err());
824 match result.unwrap_err() {
825 ParseError::Version(msg) => {
826 let template_text = lang::FORMAT_DUPLICATE_MESSAGE_ID.split("{}").next().unwrap();
828 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
829 }
830 _ => panic!("Expected ParseError::Version"),
831 }
832 }
833
834 #[test]
835 fn test_parse_sender_not_in_nodes() {
836 let data = r#"VERSION "1.0"
838
839BU_: ECM
840
841BO_ 256 EngineData : 8 TCM
842 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
843"#;
844
845 let result = Dbc::parse(data);
846 assert!(result.is_err());
847 match result.unwrap_err() {
848 ParseError::Version(msg) => {
849 let template_text = lang::FORMAT_SENDER_NOT_IN_NODES.split("{}").next().unwrap();
851 assert!(msg.contains(template_text.trim_end()));
852 }
853 _ => panic!("Expected ParseError::Version"),
854 }
855 }
856
857 #[test]
858 fn test_parse_empty_file() {
859 let result = Dbc::parse("");
861 assert!(result.is_err());
862 match result.unwrap_err() {
863 ParseError::UnexpectedEof => {
864 }
866 _ => panic!("Expected ParseError::UnexpectedEof"),
867 }
868 }
869
870 #[test]
871 fn test_parse_missing_nodes() {
872 let data = r#"VERSION "1.0"
876
877BO_ 256 EngineData : 8 ECM
878 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
879"#;
880
881 let result = Dbc::parse(data);
882 assert!(result.is_ok());
884 let dbc = result.unwrap();
885 assert!(dbc.nodes().is_empty());
886 }
887
888 #[test]
889 fn test_parse_bytes() {
890 let data = r#"VERSION "1.0"
891
892BU_: ECM
893
894BO_ 256 Engine : 8 ECM
895 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
896"#;
897
898 let bytes = data.as_bytes();
899 let dbc = Dbc::parse_bytes(bytes).unwrap();
900 assert_eq!(
901 dbc.version().map(|v| v.to_string()),
902 Some("1.0".to_string())
903 );
904 assert_eq!(dbc.messages().len(), 1);
905 }
906
907 #[test]
908 #[cfg(feature = "alloc")]
909 fn test_parse_from_string() {
910 let data = String::from(
911 r#"VERSION "1.0"
912
913BU_: ECM
914
915BO_ 256 Engine : 8 ECM
916 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
917"#,
918 );
919
920 let dbc = Dbc::parse(&data).unwrap();
921 assert_eq!(
922 dbc.version().map(|v| v.to_string()),
923 Some("1.0".to_string())
924 );
925 assert_eq!(dbc.messages().len(), 1);
926 }
927
928 #[test]
929 fn test_parse_bytes_invalid_utf8() {
930 let invalid_bytes = &[0xFF, 0xFE, 0xFD];
932 let result = Dbc::parse_bytes(invalid_bytes);
933 assert!(result.is_err());
934 match result.unwrap_err() {
935 Error::Dbc(msg) => {
936 let template_text = lang::FORMAT_INVALID_UTF8.split("{}").next().unwrap();
938 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
939 }
940 _ => panic!("Expected Dbc error"),
941 }
942 }
943
944 #[test]
945 #[cfg(feature = "alloc")]
946 fn test_save_basic() {
947 let mut parser = Parser::new(b"VERSION \"1.0\"").unwrap();
948 let version = Version::parse(&mut parser).unwrap();
949 let nodes = NodesBuilder::new().add_node("ECM").build().unwrap();
950
951 let signal = SignalBuilder::new()
952 .name("RPM")
953 .start_bit(0)
954 .length(16)
955 .byte_order(ByteOrder::BigEndian)
956 .unsigned(true)
957 .factor(0.25)
958 .offset(0.0)
959 .min(0.0)
960 .max(8000.0)
961 .unit("rpm")
962 .receivers(ReceiversBuilder::new().broadcast().build().unwrap())
963 .build()
964 .unwrap();
965
966 let message = MessageBuilder::new()
967 .id(256)
968 .name("EngineData")
969 .dlc(8)
970 .sender("ECM")
971 .add_signal(signal)
972 .build()
973 .unwrap();
974 let messages_array = [message];
975 let dbc = Dbc::new(Some(version), nodes, &messages_array);
979
980 let saved = dbc.to_dbc_string();
981 assert!(saved.contains("VERSION \"1.0\""));
982 assert!(saved.contains("BU_: ECM"));
983 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
984 assert!(saved.contains("SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *")); }
986
987 #[test]
988 fn test_save_round_trip() {
989 let original = r#"VERSION "1.0"
990
991BU_: ECM TCM
992
993BO_ 256 EngineData : 8 ECM
994 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
995 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
996
997BO_ 512 BrakeData : 4 TCM
998 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
999"#;
1000
1001 let dbc = Dbc::parse(original).unwrap();
1002 let saved = dbc.to_dbc_string();
1003 let dbc2 = Dbc::parse(&saved).unwrap();
1004
1005 assert_eq!(
1007 dbc.version().map(|v| v.to_string()),
1008 dbc2.version().map(|v| v.to_string())
1009 );
1010 assert_eq!(dbc.messages().len(), dbc2.messages().len());
1011
1012 for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
1013 assert_eq!(msg1.id(), msg2.id());
1014 assert_eq!(msg1.name(), msg2.name());
1015 assert_eq!(msg1.dlc(), msg2.dlc());
1016 assert_eq!(msg1.sender(), msg2.sender());
1017 assert_eq!(msg1.signals().len(), msg2.signals().len());
1018
1019 for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
1020 assert_eq!(sig1.name(), sig2.name());
1021 assert_eq!(sig1.start_bit(), sig2.start_bit());
1022 assert_eq!(sig1.length(), sig2.length());
1023 assert_eq!(sig1.byte_order(), sig2.byte_order());
1024 assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
1025 assert_eq!(sig1.factor(), sig2.factor());
1026 assert_eq!(sig1.offset(), sig2.offset());
1027 assert_eq!(sig1.min(), sig2.min());
1028 assert_eq!(sig1.max(), sig2.max());
1029 assert_eq!(sig1.unit(), sig2.unit());
1030 assert_eq!(sig1.receivers(), sig2.receivers());
1031 }
1032 }
1033 }
1034
1035 #[test]
1036 #[cfg(feature = "alloc")]
1037 fn test_save_multiple_messages() {
1038 let mut parser = Parser::new(b"VERSION \"1.0\"").unwrap();
1039 let version = Version::parse(&mut parser).unwrap();
1040 let nodes = NodesBuilder::new().add_node("ECM").add_node("TCM").build().unwrap();
1041
1042 let signal1 = SignalBuilder::new()
1043 .name("RPM")
1044 .start_bit(0)
1045 .length(16)
1046 .byte_order(ByteOrder::BigEndian)
1047 .unsigned(true)
1048 .factor(0.25)
1049 .offset(0.0)
1050 .min(0.0)
1051 .max(8000.0)
1052 .unit("rpm")
1053 .receivers(Receivers::Broadcast)
1054 .build()
1055 .unwrap();
1056
1057 let signal2 = SignalBuilder::new()
1058 .name("Pressure")
1059 .start_bit(0)
1060 .length(16)
1061 .byte_order(ByteOrder::LittleEndian)
1062 .unsigned(true)
1063 .factor(0.1)
1064 .offset(0.0)
1065 .min(0.0)
1066 .max(1000.0)
1067 .unit("bar")
1068 .receivers(Receivers::None)
1069 .build()
1070 .unwrap();
1071
1072 let message1 = MessageBuilder::new()
1073 .id(256)
1074 .name("EngineData")
1075 .dlc(8)
1076 .sender("ECM")
1077 .add_signal(signal1)
1078 .build()
1079 .unwrap();
1080 let message2 = MessageBuilder::new()
1081 .id(512)
1082 .name("BrakeData")
1083 .dlc(4)
1084 .sender("TCM")
1085 .add_signal(signal2)
1086 .build()
1087 .unwrap();
1088
1089 let dbc = DbcBuilder::new()
1090 .version(version)
1091 .nodes(nodes)
1092 .add_message(message1)
1093 .add_message(message2)
1094 .build()
1095 .unwrap();
1096 let saved = dbc.to_dbc_string();
1097
1098 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
1100 assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
1101 assert!(saved.contains("SG_ RPM"));
1102 assert!(saved.contains("SG_ Pressure"));
1103 }
1104
1105 #[test]
1106 #[cfg(feature = "alloc")]
1107 fn test_dbc_too_many_messages() {
1108 let mut parser = Parser::new(b"VERSION \"1.0\"").unwrap();
1109 let version = Version::parse(&mut parser).unwrap();
1110 let nodes = NodesBuilder::new().add_node("ECM").build().unwrap();
1111 let signal = SignalBuilder::new()
1112 .name("RPM")
1113 .start_bit(0)
1114 .length(16)
1115 .byte_order(ByteOrder::BigEndian)
1116 .unsigned(true)
1117 .factor(1.0)
1118 .offset(0.0)
1119 .min(0.0)
1120 .max(100.0)
1121 .receivers(ReceiversBuilder::new().none().build().unwrap())
1122 .build()
1123 .unwrap();
1124
1125 let mut messages = Vec::new();
1127 let message_names: Vec<String> = (0..10_001).map(|i| format!("Message{i}")).collect();
1128 for i in 0..10_001 {
1129 let message = MessageBuilder::new()
1130 .id(i)
1131 .name(&message_names[i as usize])
1132 .dlc(8)
1133 .sender("ECM")
1134 .add_signal(signal.clone())
1135 .build()
1136 .unwrap();
1137 messages.push(message);
1138 }
1139
1140 let dbc = Dbc::new(Some(version), nodes, &messages);
1142 assert_eq!(dbc.messages().len(), 10_000); }
1145
1146 #[test]
1147 #[cfg(feature = "alloc")]
1148 fn test_dbc_at_message_limit() {
1149 use crate::nodes::NodesBuilder;
1150
1151 let mut parser = Parser::new(b"VERSION \"1.0\"").unwrap();
1152 let version = Version::parse(&mut parser).unwrap();
1153 let nodes = NodesBuilder::new().add_node("ECM").build().unwrap();
1154 let signal = SignalBuilder::new()
1155 .name("RPM")
1156 .start_bit(0)
1157 .length(16)
1158 .byte_order(ByteOrder::BigEndian)
1159 .unsigned(true)
1160 .factor(1.0)
1161 .offset(0.0)
1162 .min(0.0)
1163 .max(100.0)
1164 .receivers(ReceiversBuilder::new().none().build().unwrap())
1165 .build()
1166 .unwrap();
1167
1168 let mut messages = Vec::new();
1170 let message_names: Vec<String> = (0..10_000).map(|i| format!("Message{i}")).collect();
1171 for i in 0..10_000 {
1172 let message = MessageBuilder::new()
1173 .id(i)
1174 .name(&message_names[i as usize])
1175 .dlc(8)
1176 .sender("ECM")
1177 .add_signal(signal.clone())
1178 .build()
1179 .unwrap();
1180 messages.push(message);
1181 }
1182
1183 let dbc = Dbc::new(Some(version), nodes, &messages);
1187 assert_eq!(dbc.messages().len(), 10_000);
1188 }
1189
1190 #[test]
1191 fn test_parse_without_version() {
1192 let data = r#"
1194BU_: ECM
1195
1196BO_ 256 Engine : 8 ECM
1197 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1198"#;
1199 let dbc = Dbc::parse(data).unwrap();
1200 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
1201 }
1202
1203 #[test]
1204 fn test_parse_without_version_with_comment() {
1205 let data = r#"// This is a comment
1207BU_: ECM
1208
1209BO_ 256 Engine : 8 ECM
1210 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1211"#;
1212 let dbc = Dbc::parse(data).unwrap();
1213 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
1214 }
1215
1216 #[test]
1217 fn test_parse_error_with_line_number() {
1218 let data = r#"VERSION "1.0"
1220
1221BU_: ECM
1222
1223BO_ 256 Engine : 8 ECM
1224 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1225BO_ 257 Invalid : 8 ECM
1226 SG_ InvalidSignal : invalid|16@1+ (0.25,0) [0|8000] "rpm"
1227"#;
1228 let result = Dbc::parse(data);
1229 assert!(result.is_err());
1230 let err = result.unwrap_err();
1231 match err {
1233 ParseError::Version(_)
1234 | ParseError::UnexpectedEof
1235 | ParseError::Expected(_)
1236 | ParseError::InvalidChar(_) => {
1237 }
1239 _ => panic!("Expected ParseError"),
1240 };
1241 }
1243
1244 #[test]
1245 fn test_parse_error_version_with_line_number() {
1246 let data = r#"VERSION invalid
1248
1249BU_: ECM
1250"#;
1251 let result = Dbc::parse(data);
1252 assert!(result.is_err());
1253 let err = result.unwrap_err();
1254 match err {
1256 ParseError::Version(_) | ParseError::UnexpectedEof | ParseError::Expected(_) => {
1257 }
1259 _ => panic!("Expected ParseError"),
1260 };
1261 }
1263
1264 #[test]
1265 fn test_parse_with_lenient_boundary_check() {
1266 let data = r#"VERSION "1.0"
1268
1269BU_: ECM
1270
1271BO_ 256 Test : 8 ECM
1272 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
1273"#;
1274
1275 let result = Dbc::parse(data);
1277 assert!(result.is_err());
1278
1279 let options = ParseOptions::lenient();
1281 let dbc = Dbc::parse_with_options(data, options).unwrap();
1282 assert_eq!(dbc.messages().len(), 1);
1283 let message = dbc.messages().at(0).unwrap();
1284 assert_eq!(message.signals().len(), 1);
1285 assert_eq!(message.signals().at(0).unwrap().name(), "CHECKSUM");
1286 }
1287
1288 #[test]
1289 fn test_parse_with_strict_boundary_check() {
1290 let data = r#"VERSION "1.0"
1292
1293BU_: ECM
1294
1295BO_ 256 Test : 8 ECM
1296 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
1297"#;
1298
1299 let result = Dbc::parse(data);
1301 assert!(result.is_err());
1302
1303 let options = ParseOptions::new();
1305 let result = Dbc::parse_with_options(data, options);
1306 assert!(result.is_err());
1307 }
1308}