1#[cfg(any(feature = "alloc", feature = "kernel"))]
2use crate::dbc::ValueDescriptionsList;
3#[cfg(any(feature = "alloc", feature = "kernel"))]
4use crate::value_descriptions::ValueDescriptions;
5#[cfg(feature = "alloc")]
6use crate::{Error, Result};
7use crate::{
8 Message, MessageList, Nodes, ParseOptions, Parser, Signal, Signals, Version,
9 error::{ParseError, ParseResult},
10};
11
12#[cfg(feature = "alloc")]
13use alloc::string::String;
14
15#[derive(Debug)]
40pub struct Dbc<'a> {
41 version: Option<Version<'a>>,
42 nodes: Nodes<'a>,
43 messages: MessageList<'a>,
44 #[cfg(any(feature = "alloc", feature = "kernel"))]
45 value_descriptions: crate::dbc::ValueDescriptionsList<'a>,
46}
47
48impl<'a> Dbc<'a> {
49 #[cfg(any(feature = "alloc", feature = "kernel"))]
51 pub(crate) fn validate(
52 nodes: &Nodes<'_>,
53 messages: &[Option<Message<'_>>],
54 message_count: usize,
55 value_descriptions: Option<&crate::dbc::ValueDescriptionsList<'_>>,
56 ) -> ParseResult<()> {
57 Self::validate_common(nodes, messages, message_count)?;
58
59 if let Some(value_descriptions) = value_descriptions {
61 let messages_slice = &messages[..message_count];
62 for ((message_id_opt, signal_name), _) in value_descriptions.iter() {
64 if let Some(message_id) = message_id_opt {
66 let message_exists = messages_slice
67 .iter()
68 .any(|msg_opt| msg_opt.as_ref().is_some_and(|msg| msg.id() == message_id));
69 if !message_exists {
70 return Err(ParseError::Message(
71 crate::error::lang::VALUE_DESCRIPTION_MESSAGE_NOT_FOUND,
72 ));
73 }
74
75 let signal_exists = messages_slice.iter().any(|msg_opt| {
77 msg_opt.as_ref().is_some_and(|msg| {
78 msg.id() == message_id && msg.signals().find(signal_name).is_some()
79 })
80 });
81 if !signal_exists {
82 return Err(ParseError::Message(
83 crate::error::lang::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND,
84 ));
85 }
86 } else {
87 let signal_exists = messages_slice.iter().any(|msg_opt| {
89 msg_opt
90 .as_ref()
91 .is_some_and(|msg| msg.signals().find(signal_name).is_some())
92 });
93 if !signal_exists {
94 return Err(ParseError::Message(
95 crate::error::lang::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND,
96 ));
97 }
98 }
99 }
100 }
101
102 Ok(())
103 }
104
105 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
107 pub(crate) fn validate(
108 nodes: &Nodes<'_>,
109 messages: &[Option<Message<'_>>],
110 message_count: usize,
111 ) -> ParseResult<()> {
112 Self::validate_common(nodes, messages, message_count)
113 }
114
115 fn validate_common(
117 nodes: &Nodes<'_>,
118 messages: &[Option<Message<'_>>],
119 message_count: usize,
120 ) -> ParseResult<()> {
121 let messages_slice = &messages[..message_count];
123 for (i, msg1_opt) in messages_slice.iter().enumerate() {
124 let msg1 = match msg1_opt {
125 Some(m) => m,
126 None => continue, };
128 for msg2_opt in messages_slice.iter().skip(i + 1) {
129 let msg2 = match msg2_opt {
130 Some(m) => m,
131 None => continue, };
133 if msg1.id() == msg2.id() {
134 return Err(ParseError::Message(
135 crate::error::lang::DUPLICATE_MESSAGE_ID,
136 ));
137 }
138 }
139 }
140
141 if !nodes.is_empty() {
144 for msg_opt in messages_slice {
145 let msg = match msg_opt {
146 Some(m) => m,
147 None => continue, };
149 if !nodes.contains(msg.sender()) {
150 return Err(ParseError::Message(crate::error::lang::SENDER_NOT_IN_NODES));
151 }
152 }
153 }
154
155 Ok(())
156 }
157
158 #[cfg(any(feature = "alloc", feature = "kernel"))]
159 pub(crate) fn new(
160 version: Option<Version<'a>>,
161 nodes: Nodes<'a>,
162 messages: &'a [Message<'a>],
163 value_descriptions: crate::dbc::value_descriptions_list::ValueDescriptionsList<'a>,
164 ) -> Self {
165 Self {
167 version,
168 nodes,
169 messages: MessageList::from_messages_slice(messages),
170 value_descriptions,
171 }
172 }
173
174 #[cfg(any(feature = "alloc", feature = "kernel"))]
175 fn new_from_options_with_value_descriptions(
176 version: Option<Version<'a>>,
177 nodes: Nodes<'a>,
178 messages: &[Option<Message<'a>>],
179 message_count: usize,
180 value_descriptions: crate::dbc::value_descriptions_list::ValueDescriptionsList<'a>,
181 ) -> Self {
182 Self {
184 version,
185 nodes,
186 messages: MessageList::from_options_slice(messages, message_count),
187 value_descriptions,
188 }
189 }
190
191 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
192 fn new_from_options(
193 version: Option<Version<'a>>,
194 nodes: Nodes<'a>,
195 messages: &[Option<Message<'a>>],
196 message_count: usize,
197 ) -> Self {
198 Self {
200 version,
201 nodes,
202 messages: MessageList::from_options_slice(messages, message_count),
203 }
204 }
205
206 #[inline]
221 #[must_use]
222 pub fn version(&self) -> Option<&Version<'a>> {
223 self.version.as_ref()
224 }
225
226 #[inline]
244 #[must_use]
245 pub fn nodes(&self) -> &Nodes<'a> {
246 &self.nodes
247 }
248
249 #[inline]
265 #[must_use]
266 pub fn messages(&self) -> &MessageList<'a> {
267 &self.messages
268 }
269
270 #[cfg(any(feature = "alloc", feature = "kernel"))]
313 #[inline]
314 #[must_use]
315 pub fn value_descriptions(&self) -> &ValueDescriptionsList<'a> {
316 &self.value_descriptions
317 }
318
319 #[cfg(any(feature = "alloc", feature = "kernel"))]
320 #[must_use]
321 pub fn value_descriptions_for_signal(
322 &self,
323 message_id: u32,
324 signal_name: &str,
325 ) -> Option<&ValueDescriptions<'a>> {
326 self.value_descriptions.for_signal(message_id, signal_name)
327 }
328
329 pub fn parse(data: &'a str) -> ParseResult<Self> {
348 Self::parse_with_options(data, ParseOptions::default())
349 }
350
351 pub fn parse_with_options(data: &'a str, options: ParseOptions) -> ParseResult<Self> {
377 let mut parser1 = Parser::new(data.as_bytes())?;
379 let _ = MessageList::count_messages_and_signals(&mut parser1)?;
380
381 let mut parser2 = Parser::new(data.as_bytes())?;
383
384 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
388 let mut messages_buffer = MessageList::new_parse_buffer();
389
390 #[cfg(any(feature = "alloc", feature = "kernel"))]
391 let mut messages_buffer: alloc::vec::Vec<Option<Message<'a>>> = {
392 use crate::compat::vec_with_capacity;
393 vec_with_capacity(MessageList::max_capacity())
394 };
395
396 let mut message_count_actual = 0;
397
398 use crate::{
400 BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SIG_GROUP_,
401 SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
402 };
403
404 let mut version: Option<Version<'a>> = None;
405 let mut nodes: Option<Nodes<'a>> = None;
406
407 #[cfg(any(feature = "alloc", feature = "kernel"))]
409 type ValueDescriptionsBufferEntry<'a> =
410 (Option<u32>, &'a str, alloc::vec::Vec<(u64, &'a str)>);
411 #[cfg(any(feature = "alloc", feature = "kernel"))]
412 let mut value_descriptions_buffer: alloc::vec::Vec<
413 ValueDescriptionsBufferEntry<'a>,
414 > = alloc::vec::Vec::new();
415
416 loop {
417 parser2.skip_newlines_and_spaces();
419 if parser2.starts_with(b"//") {
420 parser2.skip_to_end_of_line();
421 continue;
422 }
423
424 let keyword_result = parser2.peek_next_keyword();
425 let keyword = match keyword_result {
426 Ok(kw) => kw,
427 Err(ParseError::UnexpectedEof) => break,
428 Err(ParseError::Expected(_)) => {
429 if parser2.starts_with(b"//") {
430 parser2.skip_to_end_of_line();
431 continue;
432 }
433 return Err(keyword_result.unwrap_err());
434 }
435 Err(e) => return Err(e),
436 };
437
438 let pos_at_keyword = parser2.pos();
440
441 match keyword {
442 NS_ => {
443 parser2
445 .expect(crate::NS_.as_bytes())
446 .map_err(|_| ParseError::Expected("Failed to consume NS_ keyword"))?;
447 parser2.skip_newlines_and_spaces();
448 let _ = parser2.expect(b":").ok();
449 loop {
450 parser2.skip_newlines_and_spaces();
451 if parser2.is_empty() {
452 break;
453 }
454 if parser2.starts_with(b" ") || parser2.starts_with(b"\t") {
455 parser2.skip_to_end_of_line();
456 continue;
457 }
458 if parser2.starts_with(b"//") {
459 parser2.skip_to_end_of_line();
460 continue;
461 }
462 if parser2.starts_with(BS_.as_bytes())
463 || parser2.starts_with(BU_.as_bytes())
464 || parser2.starts_with(BO_.as_bytes())
465 || parser2.starts_with(SG_.as_bytes())
466 || parser2.starts_with(VERSION.as_bytes())
467 {
468 break;
469 }
470 parser2.skip_to_end_of_line();
471 }
472 continue;
473 }
474 CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | SIG_GROUP_
475 | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
476 let _ = parser2.expect(keyword.as_bytes()).ok();
478 parser2.skip_to_end_of_line();
479 continue;
480 }
481 VAL_ => {
482 #[cfg(any(feature = "alloc", feature = "kernel"))]
483 {
484 let _ = parser2.expect(crate::VAL_.as_bytes()).ok();
486 parser2.skip_newlines_and_spaces();
490 let message_id = match parser2.parse_i64() {
491 Ok(id) => {
492 if id == -1 {
494 None
495 } else if id >= 0 && id <= u32::MAX as i64 {
496 Some(id as u32)
497 } else {
498 parser2.skip_to_end_of_line();
499 continue;
500 }
501 }
502 Err(_) => {
503 parser2.skip_to_end_of_line();
504 continue;
505 }
506 };
507 parser2.skip_newlines_and_spaces();
508 let signal_name = match parser2.parse_identifier() {
509 Ok(name) => name,
510 Err(_) => {
511 parser2.skip_to_end_of_line();
512 continue;
513 }
514 };
515 let mut entries = alloc::vec::Vec::new();
517 loop {
518 parser2.skip_newlines_and_spaces();
519 if parser2.starts_with(b";") {
521 parser2.expect(b";").ok();
522 break;
523 }
524 let value = match parser2.parse_i64() {
528 Ok(v) => {
529 if v == -1 { 0xFFFF_FFFFu64 } else { v as u64 }
531 }
532 Err(_) => {
533 parser2.skip_to_end_of_line();
534 break;
535 }
536 };
537 parser2.skip_newlines_and_spaces();
538 if parser2.expect(b"\"").is_err() {
540 parser2.skip_to_end_of_line();
541 break;
542 }
543 let description_bytes = match parser2.take_until_quote(false, 1024) {
544 Ok(bytes) => bytes,
545 Err(_) => {
546 parser2.skip_to_end_of_line();
547 break;
548 }
549 };
550 let description = match core::str::from_utf8(description_bytes) {
551 Ok(s) => s,
552 Err(_) => {
553 parser2.skip_to_end_of_line();
554 break;
555 }
556 };
557 entries.push((value, description));
558 }
559 if !entries.is_empty() {
560 value_descriptions_buffer.push((message_id, signal_name, entries));
561 }
562 }
563 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
564 {
565 let _ = parser2.expect(crate::VAL_.as_bytes()).ok();
567 parser2.skip_to_end_of_line();
568 }
569 continue;
570 }
571 VERSION => {
572 version = Some(Version::parse(&mut parser2)?);
574 continue;
575 }
576 BU_ => {
577 parser2.skip_to_end_of_line();
579 let bu_input = &data.as_bytes()[pos_at_keyword..parser2.pos()];
580 let mut bu_parser = Parser::new(bu_input)?;
581 nodes = Some(Nodes::parse(&mut bu_parser)?);
582 continue;
583 }
584 BO_ => {
585 if message_count_actual >= MessageList::max_capacity() {
587 return Err(ParseError::Nodes(crate::error::lang::NODES_TOO_MANY));
588 }
589
590 let message_start_pos = pos_at_keyword;
592
593 let header_line_end = {
596 let mut temp_parser = Parser::new(&data.as_bytes()[pos_at_keyword..])?;
598 temp_parser.expect(crate::BO_.as_bytes()).ok();
600 temp_parser.skip_whitespace().ok();
601 temp_parser.parse_u32().ok(); temp_parser.skip_whitespace().ok();
603 temp_parser.parse_identifier().ok(); temp_parser.skip_whitespace().ok();
605 temp_parser.expect(b":").ok(); temp_parser.skip_whitespace().ok();
607 temp_parser.parse_u8().ok(); temp_parser.skip_whitespace().ok();
609 temp_parser.parse_identifier().ok(); pos_at_keyword + temp_parser.pos()
611 };
612
613 parser2.skip_to_end_of_line(); #[cfg(not(any(feature = "alloc", feature = "kernel")))]
618 let mut signals_array = Signals::new_parse_buffer();
619
620 #[cfg(any(feature = "alloc", feature = "kernel"))]
621 let mut signals_array: alloc::vec::Vec<Option<Signal<'a>>> = {
622 use crate::compat::vec_with_capacity;
623 vec_with_capacity(Signals::max_capacity())
624 };
625
626 let mut signal_count = 0;
627 loop {
628 parser2.skip_newlines_and_spaces();
629 if parser2.starts_with(crate::SG_.as_bytes()) {
630 if let Some(next_byte) = parser2.peek_byte_at(3) {
631 if matches!(next_byte, b' ' | b'\n' | b'\r' | b'\t') {
632 if signal_count >= Signals::max_capacity() {
633 return Err(ParseError::Receivers(
634 crate::error::lang::SIGNAL_RECEIVERS_TOO_MANY,
635 ));
636 }
637 let signal = Signal::parse(&mut parser2)?;
639 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
640 {
641 signals_array[signal_count] = Some(signal);
642 }
643 #[cfg(any(feature = "alloc", feature = "kernel"))]
644 {
645 signals_array.push(Some(signal));
646 }
647 signal_count += 1;
648 continue;
649 }
650 }
651 }
652 break;
653 }
654
655 let message_input = &data.as_bytes()[message_start_pos..header_line_end];
659 let mut message_parser = Parser::new(message_input)?;
660
661 let signals_slice: &[Option<Signal<'a>>] = {
663 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
664 {
665 &signals_array[..signal_count]
666 }
667 #[cfg(any(feature = "alloc", feature = "kernel"))]
668 {
669 &signals_array[..]
670 }
671 };
672 let message =
673 Message::parse(&mut message_parser, signals_slice, signal_count, options)?;
674
675 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
676 {
677 messages_buffer[message_count_actual] = Some(message);
678 }
679 #[cfg(any(feature = "alloc", feature = "kernel"))]
680 {
681 messages_buffer.push(Some(message));
682 }
683 message_count_actual += 1;
684 continue;
685 }
686 SG_ => {
687 parser2.skip_to_end_of_line();
689 continue;
690 }
691 _ => {
692 parser2.skip_to_end_of_line();
693 continue;
694 }
695 }
696 }
697
698 let nodes = nodes.unwrap_or_default();
700
701 let version = version.or_else(|| {
703 static EMPTY_VERSION: &[u8] = b"VERSION \"\"";
704 let mut parser = Parser::new(EMPTY_VERSION).ok()?;
705 Version::parse(&mut parser).ok()
706 });
707
708 #[cfg(any(feature = "alloc", feature = "kernel"))]
710 let value_descriptions_list = {
711 use crate::value_descriptions::ValueDescriptions;
712 use alloc::collections::BTreeMap;
713 let mut map = BTreeMap::new();
714 for (message_id, signal_name, entries) in value_descriptions_buffer {
715 let key = (message_id, signal_name);
716 let value_descriptions = ValueDescriptions::from_slice(&entries);
717 map.insert(key, value_descriptions);
718 }
719 crate::dbc::value_descriptions_list::ValueDescriptionsList::from_map(map)
720 };
721
722 let messages_slice: &[Option<Message<'a>>] = {
724 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
725 {
726 &messages_buffer[..message_count_actual]
727 }
728 #[cfg(any(feature = "alloc", feature = "kernel"))]
729 {
730 &messages_buffer[..]
731 }
732 };
733
734 #[cfg(any(feature = "alloc", feature = "kernel"))]
736 {
737 Self::validate(
738 &nodes,
739 messages_slice,
740 message_count_actual,
741 Some(&value_descriptions_list),
742 )?;
743 }
744 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
745 {
746 Self::validate(&nodes, messages_slice, message_count_actual)?;
747 }
748
749 #[cfg(any(feature = "alloc", feature = "kernel"))]
751 {
752 Ok(Self::new_from_options_with_value_descriptions(
753 version,
754 nodes,
755 messages_slice,
756 message_count_actual,
757 value_descriptions_list,
758 ))
759 }
760 #[cfg(not(any(feature = "alloc", feature = "kernel")))]
761 {
762 Ok(Self::new_from_options(
763 version,
764 nodes,
765 messages_slice,
766 message_count_actual,
767 ))
768 }
769 }
770
771 #[cfg(feature = "alloc")]
784 pub fn parse_bytes(data: &[u8]) -> Result<Dbc<'static>> {
785 let content = core::str::from_utf8(data)
786 .map_err(|_e| Error::dbc(crate::error::lang::INVALID_UTF8))?;
787 use alloc::boxed::Box;
789 let owned = String::from(content);
790 let boxed = owned.into_boxed_str();
791 let content_ref: &'static str = Box::leak(boxed);
792 Dbc::parse(content_ref).map_err(Error::ParseError)
793 }
794
795 #[cfg(feature = "std")]
817 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Dbc<'static>> {
818 let file =
819 std::fs::File::open(path).map_err(|_e| Error::dbc(crate::error::lang::READ_FAILED))?;
820 Self::from_reader(file)
821 }
822
823 #[cfg(feature = "std")]
838 pub fn from_reader<R: std::io::Read>(mut reader: R) -> Result<Dbc<'static>> {
839 let mut buffer = String::new();
840 std::io::Read::read_to_string(&mut reader, &mut buffer)
841 .map_err(|_e| Error::dbc(crate::error::lang::READ_FAILED))?;
842 use alloc::boxed::Box;
845 let boxed = buffer.into_boxed_str();
846 let content_ref: &'static str = Box::leak(boxed);
847 Dbc::parse(content_ref).map_err(Error::ParseError)
848 }
849
850 #[cfg(feature = "alloc")]
864 #[must_use]
865 pub fn to_dbc_string(&self) -> alloc::string::String {
866 use alloc::string::String;
867 let signal_count: usize = self.messages().iter().map(|m| m.signals().len()).sum();
870 let estimated_capacity = 200 + (self.messages.len() * 50) + (signal_count * 100);
871 let mut result = String::with_capacity(estimated_capacity);
872
873 if let Some(version) = &self.version {
875 result.push_str(&version.to_dbc_string());
876 result.push_str("\n\n");
877 }
878
879 result.push_str(&self.nodes.to_dbc_string());
881 result.push('\n');
882
883 for message in self.messages().iter() {
885 result.push('\n');
886 result.push_str(&message.to_dbc_string_with_signals());
887 }
888
889 result
890 }
891}
892
893#[cfg(feature = "alloc")]
894impl<'a> core::fmt::Display for Dbc<'a> {
895 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
896 write!(f, "{}", self.to_dbc_string())
897 }
898}
899
900#[cfg(all(test, feature = "std"))]
901mod tests {
902 #![allow(clippy::float_cmp)]
903 use super::*;
904 use crate::{
905 Error,
906 error::{ParseError, lang},
907 };
908
909 #[test]
910 fn parses_real_dbc() {
911 let data = r#"VERSION "1.0"
912
913BU_: ECM TCM
914
915BO_ 256 Engine : 8 ECM
916 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
917 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
918
919BO_ 512 Brake : 4 TCM
920 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
921
922 let dbc = Dbc::parse(data).unwrap();
923 assert_eq!(dbc.messages().len(), 2);
924 let mut messages_iter = dbc.messages().iter();
925 let msg0 = messages_iter.next().unwrap();
926 assert_eq!(msg0.signals().len(), 2);
927 let mut signals_iter = msg0.signals().iter();
928 assert_eq!(signals_iter.next().unwrap().name(), "RPM");
929 assert_eq!(signals_iter.next().unwrap().name(), "Temp");
930 let msg1 = messages_iter.next().unwrap();
931 assert_eq!(msg1.signals().len(), 1);
932 assert_eq!(msg1.signals().iter().next().unwrap().name(), "Pressure");
933 }
934
935 #[test]
936 fn test_parse_duplicate_message_id() {
937 let data = r#"VERSION "1.0"
939
940BU_: ECM
941
942BO_ 256 EngineData1 : 8 ECM
943 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
944
945BO_ 256 EngineData2 : 8 ECM
946 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
947"#;
948
949 let result = Dbc::parse(data);
950 assert!(result.is_err());
951 match result.unwrap_err() {
952 ParseError::Message(msg) => {
953 assert!(msg.contains(lang::DUPLICATE_MESSAGE_ID));
954 }
955 _ => panic!("Expected ParseError::Message"),
956 }
957 }
958
959 #[test]
960 fn test_parse_sender_not_in_nodes() {
961 let data = r#"VERSION "1.0"
963
964BU_: ECM
965
966BO_ 256 EngineData : 8 TCM
967 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
968"#;
969
970 let result = Dbc::parse(data);
971 assert!(result.is_err());
972 match result.unwrap_err() {
973 ParseError::Message(msg) => {
974 assert!(msg.contains(lang::SENDER_NOT_IN_NODES));
975 }
976 _ => panic!("Expected ParseError::Message"),
977 }
978 }
979
980 #[test]
981 fn test_parse_empty_file() {
982 let result = Dbc::parse("");
984 assert!(result.is_err());
985 match result.unwrap_err() {
986 ParseError::UnexpectedEof => {
987 }
989 _ => panic!("Expected ParseError::UnexpectedEof"),
990 }
991 }
992
993 #[test]
994 fn test_parse_missing_nodes() {
995 let data = r#"VERSION "1.0"
999
1000BO_ 256 EngineData : 8 ECM
1001 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
1002"#;
1003
1004 let result = Dbc::parse(data);
1005 assert!(result.is_ok());
1007 let dbc = result.unwrap();
1008 assert!(dbc.nodes().is_empty());
1009 }
1010
1011 #[test]
1012 fn test_parse_bytes() {
1013 let data = r#"VERSION "1.0"
1014
1015BU_: ECM
1016
1017BO_ 256 Engine : 8 ECM
1018 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1019"#;
1020
1021 let bytes = data.as_bytes();
1022 let dbc = Dbc::parse_bytes(bytes).unwrap();
1023 assert_eq!(
1024 dbc.version().map(|v| v.to_string()),
1025 Some("1.0".to_string())
1026 );
1027 assert_eq!(dbc.messages().len(), 1);
1028 }
1029
1030 #[test]
1031 #[cfg(feature = "alloc")]
1032 fn test_parse_from_string() {
1033 let data = String::from(
1034 r#"VERSION "1.0"
1035
1036BU_: ECM
1037
1038BO_ 256 Engine : 8 ECM
1039 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1040"#,
1041 );
1042
1043 let dbc = Dbc::parse(&data).unwrap();
1044 assert_eq!(
1045 dbc.version().map(|v| v.to_string()),
1046 Some("1.0".to_string())
1047 );
1048 assert_eq!(dbc.messages().len(), 1);
1049 }
1050
1051 #[test]
1052 fn test_parse_bytes_invalid_utf8() {
1053 let invalid_bytes = &[0xFF, 0xFE, 0xFD];
1055 let result = Dbc::parse_bytes(invalid_bytes);
1056 assert!(result.is_err());
1057 match result.unwrap_err() {
1058 Error::Dbc(msg) => {
1059 assert!(msg.contains(lang::INVALID_UTF8));
1060 }
1061 _ => panic!("Expected Dbc error"),
1062 }
1063 }
1064
1065 #[test]
1066 #[cfg(feature = "alloc")]
1067 fn test_save_basic() {
1068 let dbc_content = r#"VERSION "1.0"
1070
1071BU_: ECM
1072
1073BO_ 256 EngineData : 8 ECM
1074 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
1075"#;
1076 let dbc = Dbc::parse(dbc_content).unwrap();
1077
1078 let saved = dbc.to_dbc_string();
1079 assert!(saved.contains("VERSION \"1.0\""));
1080 assert!(saved.contains("BU_: ECM"));
1081 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
1082 assert!(saved.contains("SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *")); }
1084
1085 #[test]
1086 fn test_save_round_trip() {
1087 let original = r#"VERSION "1.0"
1088
1089BU_: ECM TCM
1090
1091BO_ 256 EngineData : 8 ECM
1092 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
1093 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
1094
1095BO_ 512 BrakeData : 4 TCM
1096 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
1097"#;
1098
1099 let dbc = Dbc::parse(original).unwrap();
1100 let saved = dbc.to_dbc_string();
1101 let dbc2 = Dbc::parse(&saved).unwrap();
1102
1103 assert_eq!(
1105 dbc.version().map(|v| v.to_string()),
1106 dbc2.version().map(|v| v.to_string())
1107 );
1108 assert_eq!(dbc.messages().len(), dbc2.messages().len());
1109
1110 for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
1111 assert_eq!(msg1.id(), msg2.id());
1112 assert_eq!(msg1.name(), msg2.name());
1113 assert_eq!(msg1.dlc(), msg2.dlc());
1114 assert_eq!(msg1.sender(), msg2.sender());
1115 assert_eq!(msg1.signals().len(), msg2.signals().len());
1116
1117 for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
1118 assert_eq!(sig1.name(), sig2.name());
1119 assert_eq!(sig1.start_bit(), sig2.start_bit());
1120 assert_eq!(sig1.length(), sig2.length());
1121 assert_eq!(sig1.byte_order(), sig2.byte_order());
1122 assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
1123 assert_eq!(sig1.factor(), sig2.factor());
1124 assert_eq!(sig1.offset(), sig2.offset());
1125 assert_eq!(sig1.min(), sig2.min());
1126 assert_eq!(sig1.max(), sig2.max());
1127 assert_eq!(sig1.unit(), sig2.unit());
1128 assert_eq!(sig1.receivers(), sig2.receivers());
1129 }
1130 }
1131 }
1132
1133 #[test]
1134 #[cfg(feature = "alloc")]
1135 fn test_save_multiple_messages() {
1136 let dbc_content = r#"VERSION "1.0"
1138
1139BU_: ECM TCM
1140
1141BO_ 256 EngineData : 8 ECM
1142 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
1143
1144BO_ 512 BrakeData : 4 TCM
1145 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar"
1146"#;
1147 let dbc = Dbc::parse(dbc_content).unwrap();
1148 let saved = dbc.to_dbc_string();
1149
1150 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
1152 assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
1153 assert!(saved.contains("SG_ RPM"));
1154 assert!(saved.contains("SG_ Pressure"));
1155 }
1156
1157 #[test]
1161 fn test_parse_without_version() {
1162 let data = r#"
1164BU_: ECM
1165
1166BO_ 256 Engine : 8 ECM
1167 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1168"#;
1169 let dbc = Dbc::parse(data).unwrap();
1170 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
1171 }
1172
1173 #[test]
1174 fn test_parse_without_version_with_comment() {
1175 let data = r#"// This is a comment
1177BU_: ECM
1178
1179BO_ 256 Engine : 8 ECM
1180 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1181"#;
1182 let dbc = Dbc::parse(data).unwrap();
1183 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
1184 }
1185
1186 #[test]
1187 fn test_parse_error_with_line_number() {
1188 let data = r#"VERSION "1.0"
1190
1191BU_: ECM
1192
1193BO_ 256 Engine : 8 ECM
1194 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
1195BO_ 257 Invalid : 8 ECM
1196 SG_ InvalidSignal : invalid|16@1+ (0.25,0) [0|8000] "rpm"
1197"#;
1198 let result = Dbc::parse(data);
1199 assert!(result.is_err());
1200 let err = result.unwrap_err();
1201 match err {
1203 ParseError::Version(_)
1204 | ParseError::Message(_)
1205 | ParseError::Nodes(_)
1206 | ParseError::Receivers(_)
1207 | ParseError::Signal(_)
1208 | ParseError::UnexpectedEof
1209 | ParseError::Expected(_)
1210 | ParseError::InvalidChar(_)
1211 | ParseError::MaxStrLength(_) => {
1212 }
1214 };
1215 }
1217
1218 #[test]
1219 fn test_parse_error_version_with_line_number() {
1220 let data = r#"VERSION invalid
1222
1223BU_: ECM
1224"#;
1225 let result = Dbc::parse(data);
1226 assert!(result.is_err());
1227 let err = result.unwrap_err();
1228 match err {
1230 ParseError::Version(_)
1231 | ParseError::Message(_)
1232 | ParseError::Nodes(_)
1233 | ParseError::Receivers(_)
1234 | ParseError::Signal(_)
1235 | ParseError::UnexpectedEof
1236 | ParseError::Expected(_)
1237 | ParseError::InvalidChar(_)
1238 | ParseError::MaxStrLength(_) => {
1239 }
1241 };
1242 }
1244
1245 #[test]
1246 fn test_parse_with_lenient_boundary_check() {
1247 let data = r#"VERSION "1.0"
1249
1250BU_: ECM
1251
1252BO_ 256 Test : 8 ECM
1253 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
1254"#;
1255
1256 let result = Dbc::parse(data);
1258 assert!(result.is_err());
1259
1260 let options = ParseOptions::lenient();
1262 let dbc = Dbc::parse_with_options(data, options).unwrap();
1263 assert_eq!(dbc.messages().len(), 1);
1264 let message = dbc.messages().at(0).unwrap();
1265 assert_eq!(message.signals().len(), 1);
1266 assert_eq!(message.signals().at(0).unwrap().name(), "CHECKSUM");
1267 }
1268
1269 #[test]
1270 fn test_parse_with_strict_boundary_check() {
1271 let data = r#"VERSION "1.0"
1273
1274BU_: ECM
1275
1276BO_ 256 Test : 8 ECM
1277 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
1278"#;
1279
1280 let result = Dbc::parse(data);
1282 assert!(result.is_err());
1283
1284 let options = ParseOptions::new();
1286 let result = Dbc::parse_with_options(data, options);
1287 assert!(result.is_err());
1288 }
1289
1290 #[test]
1291 #[cfg(any(feature = "alloc", feature = "kernel"))]
1292 fn test_parse_val_value_descriptions() {
1293 let data = r#"VERSION ""
1294
1295NS_ :
1296
1297BS_:
1298
1299BU_: Node1 Node2
1300
1301BO_ 100 Message1 : 8 Node1
1302 SG_ Signal : 32|8@1- (1,0) [-1|4] "Gear" Node2
1303
1304VAL_ 100 Signal -1 "Reverse" 0 "Neutral" 1 "First" 2 "Second" 3 "Third" 4 "Fourth" ;
1305"#;
1306
1307 let dbc = match Dbc::parse(data) {
1308 Ok(dbc) => dbc,
1309 Err(e) => panic!("Failed to parse DBC: {:?}", e),
1310 };
1311
1312 assert_eq!(dbc.messages().len(), 1);
1314 let message = dbc.messages().iter().find(|m| m.id() == 100).unwrap();
1315 assert_eq!(message.name(), "Message1");
1316 assert_eq!(message.sender(), "Node1");
1317
1318 let signal = message.signals().find("Signal").unwrap();
1320 assert_eq!(signal.name(), "Signal");
1321 assert_eq!(signal.start_bit(), 32);
1322 assert_eq!(signal.length(), 8);
1323 assert_eq!(signal.unit(), Some("Gear"));
1324
1325 let value_descriptions = dbc
1327 .value_descriptions_for_signal(100, "Signal")
1328 .expect("Value descriptions should exist for signal");
1329
1330 assert_eq!(value_descriptions.get(0xFFFFFFFF), Some("Reverse")); assert_eq!(value_descriptions.get(0), Some("Neutral"));
1333 assert_eq!(value_descriptions.get(1), Some("First"));
1334 assert_eq!(value_descriptions.get(2), Some("Second"));
1335 assert_eq!(value_descriptions.get(3), Some("Third"));
1336 assert_eq!(value_descriptions.get(4), Some("Fourth"));
1337
1338 assert_eq!(value_descriptions.get(5), None);
1340 assert_eq!(value_descriptions.get(99), None);
1341
1342 let iter = value_descriptions.iter();
1344 let mut entries: Vec<_> = iter.collect();
1345 entries.sort_by_key(|(v, _)| *v);
1346
1347 assert_eq!(entries.len(), 6);
1348 assert_eq!(entries[0], (0, "Neutral"));
1350 assert_eq!(entries[1], (1, "First"));
1351 assert_eq!(entries[2], (2, "Second"));
1352 assert_eq!(entries[3], (3, "Third"));
1353 assert_eq!(entries[4], (4, "Fourth"));
1354 assert_eq!(entries[5], (0xFFFFFFFF, "Reverse"));
1355 }
1356
1357 #[test]
1358 #[cfg(any(feature = "alloc", feature = "kernel"))]
1359 fn test_parse_val_global_value_descriptions() {
1360 let data = r#"VERSION "1.0"
1362
1363NS_ :
1364
1365 VAL_
1366
1367BS_:
1368
1369BU_: ECU DASH
1370
1371BO_ 256 EngineData: 8 ECU
1372 SG_ EngineRPM : 0|16@1+ (0.125,0) [0|8000] "rpm" Vector__XXX
1373 SG_ DI_gear : 24|3@1+ (1,0) [0|7] "" Vector__XXX
1374
1375BO_ 512 DashboardDisplay: 8 DASH
1376 SG_ DI_gear : 0|3@1+ (1,0) [0|7] "" Vector__XXX
1377 SG_ SpeedDisplay : 8|16@1+ (0.01,0) [0|300] "km/h" Vector__XXX
1378
1379VAL_ -1 DI_gear 0 "INVALID" 1 "P" 2 "R" 3 "N" 4 "D" 5 "S" 6 "L" 7 "SNA" ;
1380"#;
1381
1382 let dbc = match Dbc::parse(data) {
1383 Ok(dbc) => dbc,
1384 Err(e) => panic!("Failed to parse DBC: {:?}", e),
1385 };
1386
1387 assert_eq!(dbc.messages().len(), 2);
1389
1390 let engine_msg = dbc.messages().iter().find(|m| m.id() == 256).unwrap();
1392 assert_eq!(engine_msg.name(), "EngineData");
1393 assert_eq!(engine_msg.sender(), "ECU");
1394 let di_gear_signal1 = engine_msg.signals().find("DI_gear").unwrap();
1395 assert_eq!(di_gear_signal1.name(), "DI_gear");
1396 assert_eq!(di_gear_signal1.start_bit(), 24);
1397
1398 let dash_msg = dbc.messages().iter().find(|m| m.id() == 512).unwrap();
1400 assert_eq!(dash_msg.name(), "DashboardDisplay");
1401 assert_eq!(dash_msg.sender(), "DASH");
1402 let di_gear_signal2 = dash_msg.signals().find("DI_gear").unwrap();
1403 assert_eq!(di_gear_signal2.name(), "DI_gear");
1404 assert_eq!(di_gear_signal2.start_bit(), 0);
1405
1406 let value_descriptions1 = dbc
1408 .value_descriptions_for_signal(256, "DI_gear")
1409 .expect("Global value descriptions should exist for DI_gear in message 256");
1410
1411 assert_eq!(value_descriptions1.get(0), Some("INVALID"));
1412 assert_eq!(value_descriptions1.get(1), Some("P"));
1413 assert_eq!(value_descriptions1.get(2), Some("R"));
1414 assert_eq!(value_descriptions1.get(3), Some("N"));
1415 assert_eq!(value_descriptions1.get(4), Some("D"));
1416 assert_eq!(value_descriptions1.get(5), Some("S"));
1417 assert_eq!(value_descriptions1.get(6), Some("L"));
1418 assert_eq!(value_descriptions1.get(7), Some("SNA"));
1419
1420 let value_descriptions2 = dbc
1422 .value_descriptions_for_signal(512, "DI_gear")
1423 .expect("Global value descriptions should exist for DI_gear in message 512");
1424
1425 assert_eq!(value_descriptions2.get(0), Some("INVALID"));
1427 assert_eq!(value_descriptions2.get(1), Some("P"));
1428 assert_eq!(value_descriptions2.get(2), Some("R"));
1429 assert_eq!(value_descriptions2.get(3), Some("N"));
1430 assert_eq!(value_descriptions2.get(4), Some("D"));
1431 assert_eq!(value_descriptions2.get(5), Some("S"));
1432 assert_eq!(value_descriptions2.get(6), Some("L"));
1433 assert_eq!(value_descriptions2.get(7), Some("SNA"));
1434
1435 assert_eq!(value_descriptions1.len(), value_descriptions2.len());
1438 assert_eq!(value_descriptions1.len(), 8);
1439
1440 assert_eq!(dbc.value_descriptions_for_signal(256, "EngineRPM"), None);
1442 assert_eq!(dbc.value_descriptions_for_signal(512, "SpeedDisplay"), None);
1443 }
1444}