1use crate::{Error, Message, Nodes, Signal, Version, error::messages};
2use alloc::{string::String, string::ToString, vec::Vec};
3
4#[derive(Debug)]
19pub struct Dbc {
20 version: Version,
21 nodes: Nodes,
22 messages: Vec<Message>,
23}
24
25impl Dbc {
26 fn validate(_version: &Version, nodes: &Nodes, messages: &[Message]) -> Result<(), Error> {
28 for (i, msg1) in messages.iter().enumerate() {
30 for msg2 in messages.iter().skip(i + 1) {
31 if msg1.id() == msg2.id() {
32 return Err(Error::Dbc(messages::duplicate_message_id(
33 msg1.id(),
34 msg1.name(),
35 msg2.name(),
36 )));
37 }
38 }
39 }
40
41 for msg in messages {
43 if !nodes.contains(msg.sender()) {
44 return Err(Error::Dbc(messages::sender_not_in_nodes(
45 msg.name(),
46 msg.sender(),
47 )));
48 }
49 }
50
51 Ok(())
52 }
53
54 pub fn builder() -> DbcBuilder {
101 DbcBuilder::new()
102 }
103
104 pub(crate) fn new(
106 version: Version,
107 nodes: Nodes,
108 messages: Vec<Message>,
109 ) -> Result<Self, Error> {
110 Self::validate(&version, &nodes, &messages)?;
111
112 Ok(Self {
113 version,
114 nodes,
115 messages,
116 })
117 }
118
119 #[inline]
121 pub fn version(&self) -> &Version {
122 &self.version
123 }
124
125 #[inline]
127 pub fn nodes(&self) -> &Nodes {
128 &self.nodes
129 }
130
131 #[inline]
133 pub fn messages(&self) -> &[Message] {
134 &self.messages
135 }
136
137 pub fn parse(data: &str) -> Result<Self, Error> {
166 let mut lines = data.lines().peekable();
167
168 let version = if let Some(v) = lines.next() {
170 v
171 } else {
172 return Err(Error::Dbc(messages::DBC_EMPTY_FILE.to_string()));
173 };
174 let version = Version::parse(version)?;
175
176 let mut nodes: Option<Nodes> = None;
177 let mut messages: Vec<Message> = Vec::new();
178
179 while let Some(line) = lines.next() {
180 if line.starts_with("BU_") {
181 nodes = Some(Nodes::parse(line)?);
182 } else if line.starts_with("BO_") {
183 let message = line;
184
185 let mut signals: Vec<Signal> = Vec::with_capacity(8);
188 while let Some(signal) = lines.peek() {
189 let signal = signal.trim_start();
190
191 if signal.trim_start().starts_with("SG_") {
192 signals.push(Signal::parse(signal)?);
193 lines.next();
194 } else {
195 break;
196 }
197 }
198
199 messages.push(Message::parse(message, signals)?);
200 }
201 }
202
203 let nodes = match nodes {
204 Some(val) => val,
205 None => {
206 return Err(Error::Dbc(messages::DBC_NODES_NOT_DEFINED.to_string()));
207 }
208 };
209
210 Self::validate(&version, &nodes, &messages)?;
212
213 Ok(Self {
214 version,
215 nodes,
216 messages,
217 })
218 }
219
220 pub fn parse_bytes(data: &[u8]) -> Result<Self, Error> {
239 let content =
240 core::str::from_utf8(data).map_err(|e| Error::Dbc(messages::invalid_utf8(e)))?;
241 Self::parse(content)
242 }
243
244 pub fn parse_from<S: AsRef<str>>(data: S) -> Result<Self, Error> {
265 Self::parse(data.as_ref())
266 }
267
268 pub fn save(&self) -> String {
295 let estimated_capacity = 200
298 + (self.messages.len() * 50)
299 + (self.messages.iter().map(|m| m.signals().len()).sum::<usize>() * 100);
300 let mut result = String::with_capacity(estimated_capacity);
301
302 result.push_str(&self.version.to_dbc_string());
304 result.push_str("\n\n");
305
306 result.push_str(&self.nodes.to_dbc_string());
308 result.push('\n');
309
310 for message in &self.messages {
312 result.push('\n');
313 result.push_str(&message.to_dbc_string_with_signals());
314 }
315
316 result
317 }
318}
319
320#[cfg(feature = "std")]
321impl Dbc {
322 pub fn from_reader<R: std::io::Read>(mut reader: R) -> Result<Self, Error> {
362 use alloc::string::String;
363
364 let mut buffer = String::new();
365 std::io::Read::read_to_string(&mut reader, &mut buffer)
366 .map_err(|e| Error::Dbc(messages::read_failed(e)))?;
367 Self::parse(&buffer)
368 }
369}
370
371#[derive(Debug)]
406pub struct DbcBuilder {
407 version: Option<Version>,
408 nodes: Option<Nodes>,
409 messages: Vec<Message>,
410}
411
412impl DbcBuilder {
413 fn new() -> Self {
414 Self {
415 version: None,
416 nodes: None,
417 messages: Vec::new(),
418 }
419 }
420
421 pub fn version(mut self, version: Version) -> Self {
423 self.version = Some(version);
424 self
425 }
426
427 pub fn nodes(mut self, nodes: Nodes) -> Self {
429 self.nodes = Some(nodes);
430 self
431 }
432
433 pub fn add_message(mut self, message: Message) -> Self {
435 self.messages.push(message);
436 self
437 }
438
439 pub fn add_messages(mut self, messages: impl IntoIterator<Item = Message>) -> Self {
441 self.messages.extend(messages);
442 self
443 }
444
445 pub fn messages(mut self, messages: Vec<Message>) -> Self {
447 self.messages = messages;
448 self
449 }
450
451 pub fn clear_messages(mut self) -> Self {
453 self.messages.clear();
454 self
455 }
456
457 pub fn validate(&self) -> Result<(), Error> {
468 let version = self
469 .version
470 .as_ref()
471 .ok_or_else(|| Error::Dbc(messages::DBC_VERSION_REQUIRED.to_string()))?;
472 let nodes = self
473 .nodes
474 .as_ref()
475 .ok_or_else(|| Error::Dbc(messages::DBC_NODES_REQUIRED.to_string()))?;
476
477 Dbc::validate(version, nodes, &self.messages)
478 }
479
480 pub fn build(self) -> Result<Dbc, Error> {
488 let version = self
489 .version
490 .ok_or_else(|| Error::Dbc(messages::DBC_VERSION_REQUIRED.to_string()))?;
491 let nodes =
492 self.nodes.ok_or_else(|| Error::Dbc(messages::DBC_NODES_REQUIRED.to_string()))?;
493
494 Dbc::new(version, nodes, self.messages)
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::Dbc;
501 use crate::error::lang;
502 use crate::{ByteOrder, Error, Message, Nodes, Receivers, Signal, Version};
503
504 #[test]
505 fn test_dbc_new_valid() {
506 let version = Version::new(1, Some(0), None).unwrap();
507 let nodes = Nodes::new(["ECM", "TCM"]).unwrap();
508
509 let signal1 = Signal::new(
510 "RPM",
511 0,
512 16,
513 ByteOrder::BigEndian,
514 true,
515 0.25,
516 0.0,
517 0.0,
518 8000.0,
519 Some("rpm" as &str),
520 Receivers::Broadcast,
521 )
522 .unwrap();
523
524 let signal2 = Signal::new(
525 "Temperature",
526 16,
527 8,
528 ByteOrder::BigEndian,
529 true,
530 1.0,
531 -40.0,
532 -40.0,
533 215.0,
534 Some("°C" as &str),
535 Receivers::Broadcast,
536 )
537 .unwrap();
538
539 let message1 = Message::new(256, "EngineData", 8, "ECM", vec![signal1, signal2]).unwrap();
540 let message2 = Message::new(512, "BrakeData", 4, "TCM", vec![]).unwrap();
541
542 let dbc = Dbc::new(version, nodes, vec![message1, message2]).unwrap();
543 assert_eq!(dbc.messages().len(), 2);
544 assert_eq!(dbc.messages()[0].id(), 256);
545 assert_eq!(dbc.messages()[1].id(), 512);
546 }
547
548 #[test]
549 fn test_dbc_new_duplicate_message_id() {
550 let version = Version::new(1, Some(0), None).unwrap();
551 let nodes = Nodes::new(["ECM"]).unwrap();
552
553 let signal = Signal::new(
554 "RPM",
555 0,
556 16,
557 ByteOrder::BigEndian,
558 true,
559 1.0,
560 0.0,
561 0.0,
562 100.0,
563 None::<&str>,
564 Receivers::None,
565 )
566 .unwrap();
567
568 let message1 = Message::new(256, "EngineData1", 8, "ECM", vec![signal.clone()]).unwrap();
569 let message2 = Message::new(256, "EngineData2", 8, "ECM", vec![signal]).unwrap();
570
571 let result = Dbc::new(version, nodes, vec![message1, message2]);
572 assert!(result.is_err());
573 match result.unwrap_err() {
574 Error::Dbc(msg) => {
575 let template_text = lang::FORMAT_DUPLICATE_MESSAGE_ID.split("{}").next().unwrap();
577 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
578 }
579 _ => panic!("Expected Dbc error"),
580 }
581 }
582
583 #[test]
584 fn test_dbc_new_sender_not_in_nodes() {
585 let version = Version::new(1, Some(0), None).unwrap();
586 let nodes = Nodes::new(["ECM"]).unwrap(); let signal = Signal::new(
589 "RPM",
590 0,
591 16,
592 ByteOrder::BigEndian,
593 true,
594 1.0,
595 0.0,
596 0.0,
597 100.0,
598 None::<&str>,
599 Receivers::None,
600 )
601 .unwrap();
602
603 let message = Message::new(256, "EngineData", 8, "TCM", vec![signal]).unwrap();
604
605 let result = Dbc::new(version, nodes, vec![message]);
606 assert!(result.is_err());
607 match result.unwrap_err() {
608 Error::Dbc(msg) => {
609 let template_text = lang::FORMAT_SENDER_NOT_IN_NODES.split("{}").next().unwrap();
611 assert!(msg.contains(template_text.trim_end()));
612 }
613 _ => panic!("Expected Dbc error"),
614 }
615 }
616
617 #[test]
618 fn parses_real_dbc() {
619 let data = r#"VERSION "1.0"
620
621BU_: ECM TCM
622
623BO_ 256 Engine : 8 ECM
624 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
625 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
626
627BO_ 512 Brake : 4 TCM
628 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
629
630 let dbc = Dbc::parse(data).unwrap();
631 assert_eq!(dbc.messages().len(), 2);
632 assert_eq!(dbc.messages()[0].signals().len(), 2);
633 assert_eq!(dbc.messages()[0].signals()[0].name(), "RPM");
634 assert_eq!(dbc.messages()[0].signals()[1].name(), "Temp");
635 assert_eq!(dbc.messages()[1].signals().len(), 1);
636 assert_eq!(dbc.messages()[1].signals()[0].name(), "Pressure");
637 }
638
639 #[test]
640 fn test_parse_duplicate_message_id() {
641 let data = r#"VERSION "1.0"
643
644BU_: ECM
645
646BO_ 256 EngineData1 : 8 ECM
647 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
648
649BO_ 256 EngineData2 : 8 ECM
650 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
651"#;
652
653 let result = Dbc::parse(data);
654 assert!(result.is_err());
655 match result.unwrap_err() {
656 Error::Dbc(msg) => {
657 let template_text = lang::FORMAT_DUPLICATE_MESSAGE_ID.split("{}").next().unwrap();
659 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
660 }
661 _ => panic!("Expected Dbc error"),
662 }
663 }
664
665 #[test]
666 fn test_parse_sender_not_in_nodes() {
667 let data = r#"VERSION "1.0"
669
670BU_: ECM
671
672BO_ 256 EngineData : 8 TCM
673 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
674"#;
675
676 let result = Dbc::parse(data);
677 assert!(result.is_err());
678 match result.unwrap_err() {
679 Error::Dbc(msg) => {
680 let template_text = lang::FORMAT_SENDER_NOT_IN_NODES.split("{}").next().unwrap();
682 assert!(msg.contains(template_text.trim_end()));
683 }
684 _ => panic!("Expected Dbc error"),
685 }
686 }
687
688 #[test]
689 fn test_parse_empty_file() {
690 let result = Dbc::parse("");
692 assert!(result.is_err());
693 match result.unwrap_err() {
694 Error::Dbc(msg) => assert!(msg.contains(lang::DBC_EMPTY_FILE)),
695 _ => panic!("Expected Dbc error"),
696 }
697 }
698
699 #[test]
700 fn test_parse_missing_nodes() {
701 let data = r#"VERSION "1.0"
703
704BO_ 256 EngineData : 8 ECM
705 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
706"#;
707
708 let result = Dbc::parse(data);
709 assert!(result.is_err());
710 match result.unwrap_err() {
711 Error::Dbc(msg) => assert!(msg.contains(lang::DBC_NODES_NOT_DEFINED)),
712 _ => panic!("Expected Dbc error"),
713 }
714 }
715
716 #[test]
717 fn test_parse_bytes() {
718 let data = r#"VERSION "1.0"
719
720BU_: ECM
721
722BO_ 256 Engine : 8 ECM
723 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
724"#;
725
726 let bytes = data.as_bytes();
727 let dbc = Dbc::parse_bytes(bytes).unwrap();
728 assert_eq!(dbc.version().major(), 1);
729 assert_eq!(dbc.messages().len(), 1);
730 }
731
732 #[test]
733 fn test_parse_from_string() {
734 let data = String::from(
735 r#"VERSION "1.0"
736
737BU_: ECM
738
739BO_ 256 Engine : 8 ECM
740 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
741"#,
742 );
743
744 let dbc = Dbc::parse_from(data).unwrap();
745 assert_eq!(dbc.version().major(), 1);
746 assert_eq!(dbc.messages().len(), 1);
747 }
748
749 #[test]
750 fn test_parse_bytes_invalid_utf8() {
751 let invalid_bytes = &[0xFF, 0xFE, 0xFD];
753 let result = Dbc::parse_bytes(invalid_bytes);
754 assert!(result.is_err());
755 match result.unwrap_err() {
756 Error::Dbc(msg) => {
757 let template_text = lang::FORMAT_INVALID_UTF8.split("{}").next().unwrap();
759 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
760 }
761 _ => panic!("Expected Dbc error"),
762 }
763 }
764
765 #[test]
766 fn test_save_basic() {
767 let version = Version::new(1, Some(0), None).unwrap();
768 let nodes = Nodes::new(["ECM"]).unwrap();
769
770 let signal = Signal::new(
771 "RPM",
772 0,
773 16,
774 ByteOrder::BigEndian,
775 true,
776 0.25,
777 0.0,
778 0.0,
779 8000.0,
780 Some("rpm" as &str),
781 Receivers::Broadcast,
782 )
783 .unwrap();
784
785 let message = Message::new(256, "EngineData", 8, "ECM", vec![signal]).unwrap();
786 let dbc = Dbc::new(version, nodes, vec![message]).unwrap();
787
788 let saved = dbc.save();
789 assert!(saved.contains("VERSION \"1.0\""));
790 assert!(saved.contains("BU_: ECM"));
791 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
792 assert!(saved.contains("SG_ RPM : 0|16@1+ (0.25,0) [0|8000] \"rpm\" *"));
793 }
794
795 #[test]
796 fn test_save_round_trip() {
797 let original = r#"VERSION "1.0"
798
799BU_: ECM TCM
800
801BO_ 256 EngineData : 8 ECM
802 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
803 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
804
805BO_ 512 BrakeData : 4 TCM
806 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
807"#;
808
809 let dbc = Dbc::parse(original).unwrap();
810 let saved = dbc.save();
811 let dbc2 = Dbc::parse(&saved).unwrap();
812
813 assert_eq!(dbc.version().major(), dbc2.version().major());
815 assert_eq!(dbc.version().minor(), dbc2.version().minor());
816 assert_eq!(dbc.messages().len(), dbc2.messages().len());
817
818 for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
819 assert_eq!(msg1.id(), msg2.id());
820 assert_eq!(msg1.name(), msg2.name());
821 assert_eq!(msg1.dlc(), msg2.dlc());
822 assert_eq!(msg1.sender(), msg2.sender());
823 assert_eq!(msg1.signals().len(), msg2.signals().len());
824
825 for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
826 assert_eq!(sig1.name(), sig2.name());
827 assert_eq!(sig1.start_bit(), sig2.start_bit());
828 assert_eq!(sig1.length(), sig2.length());
829 assert_eq!(sig1.byte_order(), sig2.byte_order());
830 assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
831 assert_eq!(sig1.factor(), sig2.factor());
832 assert_eq!(sig1.offset(), sig2.offset());
833 assert_eq!(sig1.min(), sig2.min());
834 assert_eq!(sig1.max(), sig2.max());
835 assert_eq!(sig1.unit(), sig2.unit());
836 assert_eq!(sig1.receivers(), sig2.receivers());
837 }
838 }
839 }
840
841 #[test]
842 fn test_save_multiple_messages() {
843 let version = Version::new(1, Some(0), None).unwrap();
844 let nodes = Nodes::new(["ECM", "TCM"]).unwrap();
845
846 let signal1 = Signal::new(
847 "RPM",
848 0,
849 16,
850 ByteOrder::BigEndian,
851 true,
852 0.25,
853 0.0,
854 0.0,
855 8000.0,
856 Some("rpm" as &str),
857 Receivers::Broadcast,
858 )
859 .unwrap();
860
861 let signal2 = Signal::new(
862 "Pressure",
863 0,
864 16,
865 ByteOrder::LittleEndian,
866 true,
867 0.1,
868 0.0,
869 0.0,
870 1000.0,
871 Some("bar" as &str),
872 Receivers::None,
873 )
874 .unwrap();
875
876 let message1 = Message::new(256, "EngineData", 8, "ECM", vec![signal1]).unwrap();
877 let message2 = Message::new(512, "BrakeData", 4, "TCM", vec![signal2]).unwrap();
878
879 let dbc = Dbc::new(version, nodes, vec![message1, message2]).unwrap();
880 let saved = dbc.save();
881
882 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
884 assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
885 assert!(saved.contains("SG_ RPM"));
886 assert!(saved.contains("SG_ Pressure"));
887 }
888}
889
890#[cfg(feature = "std")]
891#[cfg(test)]
892mod std_tests {
893 use super::Dbc;
894 use std::io::Cursor;
895
896 #[test]
897 fn test_from_reader_cursor() {
898 let data = r#"VERSION "1.0"
899
900BU_: ECM
901
902BO_ 256 Engine : 8 ECM
903 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
904"#;
905
906 let cursor = Cursor::new(data.as_bytes());
907 let dbc = Dbc::from_reader(cursor).unwrap();
908 assert_eq!(dbc.version().major(), 1);
909 assert_eq!(dbc.messages().len(), 1);
910 }
911
912 #[test]
913 fn test_from_reader_file() {
914 let content =
916 std::fs::read_to_string("tests/data/simple.dbc").expect("Failed to read test file");
917 let cursor = Cursor::new(content.as_bytes());
918 let dbc = Dbc::from_reader(cursor).unwrap();
919 assert_eq!(dbc.messages().len(), 2);
920 }
921}