1use super::Dbc;
2use crate::Result;
3use std::fmt::{Display, Formatter, Result as FmtResult};
4use std::path::Path;
5
6impl Dbc {
7 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
19 let content = std::fs::read_to_string(path.as_ref())?;
20 Self::parse(&content)
21 }
22 #[must_use = "return value should be used"]
36 pub fn to_dbc_string(&self) -> String {
37 let signal_count: usize = self.messages().iter().map(|m| m.signals().len()).sum();
40 let estimated_capacity = 200 + (self.messages.len() * 50) + (signal_count * 100);
41 let mut result = String::with_capacity(estimated_capacity);
42
43 if let Some(version) = &self.version {
45 result.push_str(&version.to_dbc_string());
46 result.push_str("\n\n");
47 }
48
49 if let Some(ref bit_timing) = self.bit_timing {
51 result.push_str(&bit_timing.to_string());
52 result.push_str("\n\n");
53 } else {
54 result.push_str("BS_:\n\n");
56 }
57
58 result.push_str(&self.nodes.to_dbc_string());
60 result.push('\n');
61
62 for message in self.messages().iter() {
64 result.push('\n');
65 result.push_str(&message.to_string_full());
66 }
67
68 if let Some(comment) = self.comment() {
71 result.push_str("\nCM_ \"");
72 result.push_str(comment);
73 result.push_str("\";\n");
74 }
75
76 for node in self.nodes.iter_nodes() {
78 if let Some(comment) = node.comment() {
79 result.push_str("CM_ BU_ ");
80 result.push_str(node.name());
81 result.push_str(" \"");
82 result.push_str(comment);
83 result.push_str("\";\n");
84 }
85 }
86
87 for message in self.messages().iter() {
89 if let Some(comment) = message.comment() {
90 result.push_str("CM_ BO_ ");
91 result.push_str(&message.id().to_string());
92 result.push_str(" \"");
93 result.push_str(comment);
94 result.push_str("\";\n");
95 }
96
97 for signal in message.signals().iter() {
98 if let Some(comment) = signal.comment() {
99 result.push_str("CM_ SG_ ");
100 result.push_str(&message.id().to_string());
101 result.push(' ');
102 result.push_str(signal.name());
103 result.push_str(" \"");
104 result.push_str(comment);
105 result.push_str("\";\n");
106 }
107 }
108 }
109
110 result
111 }
112}
113
114impl Display for Dbc {
115 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
116 write!(f, "{}", self.to_dbc_string())
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use crate::Dbc;
123
124 #[test]
125 fn test_to_dbc_string() {
126 let dbc = Dbc::parse(
127 r#"VERSION "1.0"
128
129BU_: ECM
130
131BO_ 256 Engine : 8 ECM
132 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
133"#,
134 )
135 .unwrap();
136
137 let dbc_string = dbc.to_dbc_string();
138 assert!(dbc_string.contains("VERSION"));
139 assert!(dbc_string.contains("BU_"));
140 assert!(dbc_string.contains("BO_"));
141 assert!(dbc_string.contains("SG_"));
142 }
143
144 #[test]
145 fn test_display() {
146 let dbc = Dbc::parse(
147 r#"VERSION "1.0"
148
149BU_: ECM
150
151BO_ 256 Engine : 8 ECM
152"#,
153 )
154 .unwrap();
155
156 let display_str = format!("{}", dbc);
157 assert!(display_str.contains("VERSION"));
158 }
159
160 #[test]
161 fn test_save_round_trip() {
162 let original = r#"VERSION "1.0"
163
164BU_: ECM TCM
165
166BO_ 256 EngineData : 8 ECM
167 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
168 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
169
170BO_ 512 BrakeData : 4 TCM
171 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
172"#;
173
174 let dbc = Dbc::parse(original).unwrap();
175 let saved = dbc.to_dbc_string();
176 let dbc2 = Dbc::parse(&saved).unwrap();
177
178 assert_eq!(
180 dbc.version().map(|v| v.to_string()),
181 dbc2.version().map(|v| v.to_string())
182 );
183 assert_eq!(dbc.messages().len(), dbc2.messages().len());
184
185 for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
186 assert_eq!(msg1.id(), msg2.id());
187 assert_eq!(msg1.name(), msg2.name());
188 assert_eq!(msg1.dlc(), msg2.dlc());
189 assert_eq!(msg1.sender(), msg2.sender());
190 assert_eq!(msg1.signals().len(), msg2.signals().len());
191
192 for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
193 assert_eq!(sig1.name(), sig2.name());
194 assert_eq!(sig1.start_bit(), sig2.start_bit());
195 assert_eq!(sig1.length(), sig2.length());
196 assert_eq!(sig1.byte_order(), sig2.byte_order());
197 assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
198 assert_eq!(sig1.factor(), sig2.factor());
199 assert_eq!(sig1.offset(), sig2.offset());
200 assert_eq!(sig1.min(), sig2.min());
201 assert_eq!(sig1.max(), sig2.max());
202 assert_eq!(sig1.unit(), sig2.unit());
203 assert_eq!(sig1.receivers(), sig2.receivers());
204 }
205 }
206 }
207
208 #[test]
209 fn test_save_basic() {
210 let dbc_content = r#"VERSION "1.0"
213
214BU_: ECM
215
216BO_ 256 EngineData : 8 ECM
217 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
218"#;
219 let dbc = Dbc::parse(dbc_content).unwrap();
220
221 let saved = dbc.to_dbc_string();
222 assert!(saved.contains("VERSION \"1.0\""));
223 assert!(saved.contains("BU_: ECM"));
224 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
225 assert!(saved.contains("SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" Vector__XXX"));
227 }
228
229 #[test]
230 fn test_save_multiple_messages() {
231 let dbc_content = r#"VERSION "1.0"
233
234BU_: ECM TCM
235
236BO_ 256 EngineData : 8 ECM
237 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
238
239BO_ 512 BrakeData : 4 TCM
240 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar"
241"#;
242 let dbc = Dbc::parse(dbc_content).unwrap();
243 let saved = dbc.to_dbc_string();
244
245 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
247 assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
248 assert!(saved.contains("SG_ RPM"));
249 assert!(saved.contains("SG_ Pressure"));
250 }
251
252 #[test]
253 fn test_bit_timing_empty() {
254 let dbc = Dbc::parse(
256 r#"VERSION "1.0"
257
258BS_:
259
260BU_: ECM
261
262BO_ 256 Engine : 8 ECM
263"#,
264 )
265 .unwrap();
266
267 assert!(dbc.bit_timing().is_none());
269
270 let saved = dbc.to_dbc_string();
272 assert!(saved.contains("BS_:"));
273 }
274
275 #[test]
276 fn test_bit_timing_with_values() {
277 let dbc = Dbc::parse(
278 r#"VERSION "1.0"
279
280BS_: 500000 : 1,2
281
282BU_: ECM
283
284BO_ 256 Engine : 8 ECM
285"#,
286 )
287 .unwrap();
288
289 let bt = dbc.bit_timing().expect("bit timing should be present");
291 assert_eq!(bt.baudrate(), Some(500000));
292 assert_eq!(bt.btr1(), Some(1));
293 assert_eq!(bt.btr2(), Some(2));
294
295 let saved = dbc.to_dbc_string();
297 assert!(saved.contains("BS_: 500000 : 1,2"));
298 }
299
300 #[test]
301 fn test_bit_timing_baudrate_only() {
302 let dbc = Dbc::parse(
303 r#"VERSION "1.0"
304
305BS_: 500000
306
307BU_: ECM
308
309BO_ 256 Engine : 8 ECM
310"#,
311 )
312 .unwrap();
313
314 let bt = dbc.bit_timing().expect("bit timing should be present");
316 assert_eq!(bt.baudrate(), Some(500000));
317 assert_eq!(bt.btr1(), None);
318 assert_eq!(bt.btr2(), None);
319
320 let saved = dbc.to_dbc_string();
322 assert!(saved.contains("BS_: 500000"));
323 }
324}