dbc_rs/dbc/builder/impls.rs
1use super::DbcBuilder;
2use crate::{
3 BitTimingBuilder, Dbc, ExtendedMultiplexingBuilder, MessageBuilder, NodesBuilder, Receivers,
4 ReceiversBuilder, SignalBuilder, ValueDescriptionsBuilder, VersionBuilder,
5};
6use std::collections::BTreeMap;
7
8impl DbcBuilder {
9 /// Creates a new empty `DbcBuilder`.
10 ///
11 /// # Examples
12 ///
13 /// ```rust,no_run
14 /// use dbc_rs::{DbcBuilder, VersionBuilder, NodesBuilder, MessageBuilder};
15 ///
16 /// let dbc = DbcBuilder::new()
17 /// .version(VersionBuilder::new().version("1.0"))
18 /// .nodes(NodesBuilder::new().add_node("ECM"))
19 /// .add_message(MessageBuilder::new()
20 /// .id(512)
21 /// .name("Brake")
22 /// .dlc(4)
23 /// .sender("ECM"))
24 /// .build()?;
25 /// # Ok::<(), dbc_rs::Error>(())
26 /// ```
27 pub fn new() -> Self {
28 Self {
29 version: VersionBuilder::new(),
30 bit_timing: None,
31 nodes: NodesBuilder::new(),
32 messages: Vec::new(),
33 value_descriptions: BTreeMap::new(),
34 extended_multiplexing: Vec::new(),
35 comment: None,
36 }
37 }
38
39 /// Sets the version for the DBC file.
40 ///
41 /// # Examples
42 ///
43 /// ```rust,no_run
44 /// use dbc_rs::{DbcBuilder, VersionBuilder};
45 ///
46 /// let vb = VersionBuilder::new().version("1.0");
47 /// let builder = DbcBuilder::new()
48 /// .version(vb);
49 /// # Ok::<(), dbc_rs::Error>(())
50 /// ```
51 #[must_use = "builder method returns modified builder"]
52 pub fn version(mut self, version: VersionBuilder) -> Self {
53 self.version = version;
54 self
55 }
56
57 /// Sets the bit timing configuration for the DBC file.
58 ///
59 /// The BS_ section in DBC files specifies CAN bus timing parameters.
60 /// This section is typically empty as bit timing is obsolete in modern CAN systems.
61 ///
62 /// # Examples
63 ///
64 /// ```rust,no_run
65 /// use dbc_rs::{DbcBuilder, BitTimingBuilder};
66 ///
67 /// let builder = DbcBuilder::new()
68 /// .bit_timing(BitTimingBuilder::new().baudrate(500000));
69 /// # Ok::<(), dbc_rs::Error>(())
70 /// ```
71 #[must_use = "builder method returns modified builder"]
72 pub fn bit_timing(mut self, bit_timing: BitTimingBuilder) -> Self {
73 self.bit_timing = Some(bit_timing);
74 self
75 }
76
77 /// Sets the nodes (ECUs) for the DBC file.
78 ///
79 /// # Examples
80 ///
81 /// ```rust,no_run
82 /// use dbc_rs::{DbcBuilder, NodesBuilder};
83 ///
84 /// let builder = DbcBuilder::new()
85 /// .nodes(NodesBuilder::new().add_node("ECM"));
86 /// # Ok::<(), dbc_rs::Error>(())
87 /// ```
88 #[must_use = "builder method returns modified builder"]
89 pub fn nodes(mut self, nodes: NodesBuilder) -> Self {
90 self.nodes = nodes;
91 self
92 }
93
94 /// Adds a message to the DBC file.
95 ///
96 /// # Examples
97 ///
98 /// ```rust,no_run
99 /// use dbc_rs::{DbcBuilder, MessageBuilder};
100 ///
101 /// let message = MessageBuilder::new()
102 /// .id(256)
103 /// .name("EngineData")
104 /// .dlc(8)
105 /// .sender("ECM");
106 ///
107 /// let builder = DbcBuilder::new()
108 /// .add_message(message);
109 /// # Ok::<(), dbc_rs::Error>(())
110 /// ```
111 #[must_use = "builder method returns modified builder"]
112 pub fn add_message(mut self, message: MessageBuilder) -> Self {
113 self.messages.push(message);
114 self
115 }
116
117 /// Adds multiple messages to the DBC file.
118 ///
119 /// # Examples
120 ///
121 /// ```rust,no_run
122 /// use dbc_rs::{DbcBuilder, MessageBuilder};
123 ///
124 /// let msg1 = MessageBuilder::new().id(256).name("Msg1").dlc(8).sender("ECM");
125 /// let msg2 = MessageBuilder::new().id(512).name("Msg2").dlc(4).sender("TCM");
126 ///
127 /// let builder = DbcBuilder::new()
128 /// .add_messages(vec![msg1, msg2]);
129 /// # Ok::<(), dbc_rs::Error>(())
130 /// ```
131 #[must_use = "builder method returns modified builder"]
132 pub fn add_messages(mut self, messages: impl IntoIterator<Item = MessageBuilder>) -> Self {
133 self.messages.extend(messages);
134 self
135 }
136
137 /// Clears all messages from the builder.
138 ///
139 /// # Examples
140 ///
141 /// ```rust,no_run
142 /// use dbc_rs::DbcBuilder;
143 ///
144 /// let builder = DbcBuilder::new()
145 /// .clear_messages();
146 /// ```
147 #[must_use = "builder method returns modified builder"]
148 pub fn clear_messages(mut self) -> Self {
149 self.messages.clear();
150 self
151 }
152
153 /// Adds value descriptions for a signal in a message.
154 ///
155 /// Value descriptions (VAL_) map numeric signal values to human-readable text.
156 ///
157 /// # Arguments
158 ///
159 /// * `message_id` - The CAN message ID containing the signal
160 /// * `signal_name` - The name of the signal
161 /// * `value_descriptions` - The value descriptions builder
162 ///
163 /// # Examples
164 ///
165 /// ```rust,no_run
166 /// use dbc_rs::{DbcBuilder, ValueDescriptionsBuilder};
167 ///
168 /// let value_desc = ValueDescriptionsBuilder::new()
169 /// .add_entry(0, "Park")
170 /// .add_entry(1, "Drive");
171 ///
172 /// let builder = DbcBuilder::new()
173 /// .add_value_description(256, "Gear", value_desc);
174 /// # Ok::<(), dbc_rs::Error>(())
175 /// ```
176 #[must_use = "builder method returns modified builder"]
177 pub fn add_value_description(
178 mut self,
179 message_id: u32,
180 signal_name: impl AsRef<str>,
181 value_descriptions: ValueDescriptionsBuilder,
182 ) -> Self {
183 self.value_descriptions.insert(
184 (Some(message_id), signal_name.as_ref().to_string()),
185 value_descriptions,
186 );
187 self
188 }
189
190 /// Adds global value descriptions for a signal (applies to all messages with this signal).
191 ///
192 /// Global value descriptions (VAL_ with message_id -1) apply to signals with the given
193 /// name in any message.
194 ///
195 /// # Arguments
196 ///
197 /// * `signal_name` - The name of the signal
198 /// * `value_descriptions` - The value descriptions builder
199 ///
200 /// # Examples
201 ///
202 /// ```rust,no_run
203 /// use dbc_rs::{DbcBuilder, ValueDescriptionsBuilder};
204 ///
205 /// let value_desc = ValueDescriptionsBuilder::new()
206 /// .add_entry(0, "Off")
207 /// .add_entry(1, "On");
208 ///
209 /// let builder = DbcBuilder::new()
210 /// .add_global_value_description("Status", value_desc);
211 /// # Ok::<(), dbc_rs::Error>(())
212 /// ```
213 #[must_use = "builder method returns modified builder"]
214 pub fn add_global_value_description(
215 mut self,
216 signal_name: impl AsRef<str>,
217 value_descriptions: ValueDescriptionsBuilder,
218 ) -> Self {
219 self.value_descriptions
220 .insert((None, signal_name.as_ref().to_string()), value_descriptions);
221 self
222 }
223
224 /// Clears all value descriptions from the builder.
225 ///
226 /// # Examples
227 ///
228 /// ```rust,no_run
229 /// use dbc_rs::DbcBuilder;
230 ///
231 /// let builder = DbcBuilder::new()
232 /// .clear_value_descriptions();
233 /// ```
234 #[must_use = "builder method returns modified builder"]
235 pub fn clear_value_descriptions(mut self) -> Self {
236 self.value_descriptions.clear();
237 self
238 }
239
240 /// Adds an extended multiplexing entry to the DBC file.
241 ///
242 /// Extended multiplexing (SG_MUL_VAL_) entries define which multiplexer switch
243 /// values activate specific multiplexed signals, allowing for range-based activation.
244 ///
245 /// # Examples
246 ///
247 /// ```rust,no_run
248 /// use dbc_rs::{DbcBuilder, ExtendedMultiplexingBuilder};
249 ///
250 /// let ext_mux = ExtendedMultiplexingBuilder::new()
251 /// .message_id(500)
252 /// .signal_name("Signal_A")
253 /// .multiplexer_switch("Mux1")
254 /// .add_value_range(0, 5);
255 ///
256 /// let builder = DbcBuilder::new()
257 /// .add_extended_multiplexing(ext_mux);
258 /// # Ok::<(), dbc_rs::Error>(())
259 /// ```
260 #[must_use = "builder method returns modified builder"]
261 pub fn add_extended_multiplexing(mut self, ext_mux: ExtendedMultiplexingBuilder) -> Self {
262 self.extended_multiplexing.push(ext_mux);
263 self
264 }
265
266 /// Adds multiple extended multiplexing entries to the DBC file.
267 ///
268 /// # Examples
269 ///
270 /// ```rust,no_run
271 /// use dbc_rs::{DbcBuilder, ExtendedMultiplexingBuilder};
272 ///
273 /// let ext_mux1 = ExtendedMultiplexingBuilder::new()
274 /// .message_id(500)
275 /// .signal_name("Signal_A")
276 /// .multiplexer_switch("Mux1")
277 /// .add_value_range(0, 5);
278 /// let ext_mux2 = ExtendedMultiplexingBuilder::new()
279 /// .message_id(500)
280 /// .signal_name("Signal_B")
281 /// .multiplexer_switch("Mux1")
282 /// .add_value_range(10, 15);
283 ///
284 /// let builder = DbcBuilder::new()
285 /// .add_extended_multiplexings(vec![ext_mux1, ext_mux2]);
286 /// # Ok::<(), dbc_rs::Error>(())
287 /// ```
288 #[must_use = "builder method returns modified builder"]
289 pub fn add_extended_multiplexings(
290 mut self,
291 ext_muxes: impl IntoIterator<Item = ExtendedMultiplexingBuilder>,
292 ) -> Self {
293 self.extended_multiplexing.extend(ext_muxes);
294 self
295 }
296
297 /// Clears all extended multiplexing entries from the builder.
298 ///
299 /// # Examples
300 ///
301 /// ```rust,no_run
302 /// use dbc_rs::DbcBuilder;
303 ///
304 /// let builder = DbcBuilder::new()
305 /// .clear_extended_multiplexing();
306 /// ```
307 #[must_use = "builder method returns modified builder"]
308 pub fn clear_extended_multiplexing(mut self) -> Self {
309 self.extended_multiplexing.clear();
310 self
311 }
312
313 /// Sets the database-level comment (general CM_ entry).
314 ///
315 /// # Examples
316 ///
317 /// ```rust,no_run
318 /// use dbc_rs::DbcBuilder;
319 ///
320 /// let builder = DbcBuilder::new()
321 /// .comment("CAN database for powertrain");
322 /// ```
323 #[must_use = "builder method returns modified builder"]
324 pub fn comment(mut self, comment: impl AsRef<str>) -> Self {
325 self.comment = Some(comment.as_ref().to_string());
326 self
327 }
328
329 /// Creates a `DbcBuilder` from an existing `Dbc`.
330 ///
331 /// This allows you to modify an existing DBC file by creating a builder
332 /// initialized with all data from the provided DBC.
333 ///
334 /// # Arguments
335 ///
336 /// * `dbc` - The existing `Dbc` to create a builder from
337 ///
338 /// # Examples
339 ///
340 /// ```rust,no_run
341 /// use dbc_rs::{Dbc, DbcBuilder, MessageBuilder};
342 ///
343 /// let original = Dbc::parse(r#"VERSION "1.0"\nBU_: ECM\n"#)?;
344 /// let modified = DbcBuilder::from_dbc(&original)
345 /// .add_message(MessageBuilder::new().id(256).name("Msg").dlc(8).sender("ECM"))
346 /// .build()?;
347 /// # Ok::<(), dbc_rs::Error>(())
348 /// ```
349 pub fn from_dbc(dbc: &Dbc) -> Self {
350 // Convert version to builder (store builder, not final type)
351 let version = if let Some(v) = dbc.version() {
352 VersionBuilder::new().version(v.as_str())
353 } else {
354 VersionBuilder::new()
355 };
356
357 // Copy bit timing - convert to builder
358 let bit_timing = dbc.bit_timing().map(|bt| {
359 let mut builder = BitTimingBuilder::new();
360 if let Some(baudrate) = bt.baudrate() {
361 builder = builder.baudrate(baudrate);
362 }
363 if let Some(btr1) = bt.btr1() {
364 builder = builder.btr1(btr1);
365 }
366 if let Some(btr2) = bt.btr2() {
367 builder = builder.btr2(btr2);
368 }
369 builder
370 });
371
372 // Convert nodes to builder (store builder, not final type)
373 // Note: We unwrap here because we're converting from a valid Dbc, so names should already fit MAX_NAME_SIZE
374 let nodes = {
375 let mut builder = NodesBuilder::new();
376 for node in dbc.nodes().iter() {
377 // Convert compat::String to std::string::String for the builder
378 let node_str = node.to_string();
379 // Should never fail for valid Dbc - unwrap is safe
380 builder = builder.add_node(node_str);
381 }
382 builder
383 };
384
385 // Convert messages to builders (store builders, not final types)
386 let messages: Vec<MessageBuilder> = dbc
387 .messages()
388 .iter()
389 .map(|msg| {
390 let mut msg_builder = MessageBuilder::new()
391 .id(msg.id())
392 .name(msg.name())
393 .dlc(msg.dlc())
394 .sender(msg.sender());
395
396 // Convert signals using SignalBuilder
397 for sig in msg.signals().iter() {
398 let mut sig_builder = SignalBuilder::new()
399 .name(sig.name())
400 .start_bit(sig.start_bit())
401 .length(sig.length())
402 .byte_order(sig.byte_order())
403 .unsigned(sig.is_unsigned())
404 .factor(sig.factor())
405 .offset(sig.offset())
406 .min(sig.min())
407 .max(sig.max());
408
409 if let Some(unit) = sig.unit() {
410 sig_builder = sig_builder.unit(unit);
411 }
412
413 // Convert receivers using ReceiversBuilder
414 let receivers_builder = match sig.receivers() {
415 Receivers::None => ReceiversBuilder::new().none(),
416 Receivers::Nodes(nodes) => {
417 let mut rb = ReceiversBuilder::new();
418 // nodes is Vec<String<{MAX_NAME_SIZE}>>, iterate directly
419 for receiver in nodes.iter() {
420 // receiver is &String<{MAX_NAME_SIZE}>, clone it
421 let receiver_str = receiver.clone();
422 // Should never fail for valid Dbc - unwrap is safe
423 rb = rb.add_node(receiver_str);
424 }
425 rb
426 }
427 };
428 sig_builder = sig_builder.receivers(receivers_builder);
429
430 msg_builder = msg_builder.add_signal(sig_builder);
431 }
432
433 msg_builder
434 })
435 .collect();
436
437 // Convert value descriptions from Dbc to builder format (store builders, not final types)
438 let mut value_descriptions: BTreeMap<(Option<u32>, String), ValueDescriptionsBuilder> =
439 BTreeMap::new();
440 for ((message_id, signal_name), vd) in dbc.value_descriptions().iter() {
441 // Store as String and ValueDescriptionsBuilder (no leak)
442 let mut builder = ValueDescriptionsBuilder::new();
443 for (value, desc) in vd.iter() {
444 builder = builder.add_entry(value, desc);
445 }
446 value_descriptions.insert((message_id, signal_name.to_string()), builder);
447 }
448
449 // Convert extended multiplexing entries to builders
450 let extended_multiplexing: Vec<ExtendedMultiplexingBuilder> = dbc
451 .extended_multiplexing()
452 .iter()
453 .map(|ext_mux| {
454 let mut builder = ExtendedMultiplexingBuilder::new()
455 .message_id(ext_mux.message_id())
456 .signal_name(ext_mux.signal_name())
457 .multiplexer_switch(ext_mux.multiplexer_switch());
458
459 // Add all value ranges
460 for (min, max) in ext_mux.value_ranges() {
461 builder = builder.add_value_range(*min, *max);
462 }
463
464 builder
465 })
466 .collect();
467
468 // Copy comment if present
469 let comment = dbc.comment().map(|c| c.to_string());
470
471 Self {
472 version,
473 bit_timing,
474 nodes,
475 messages,
476 value_descriptions,
477 extended_multiplexing,
478 comment,
479 }
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 #![allow(clippy::float_cmp)]
486 use super::DbcBuilder;
487 use crate::{
488 ByteOrder, Dbc, ExtendedMultiplexingBuilder, MessageBuilder, NodesBuilder,
489 ReceiversBuilder, SignalBuilder, VersionBuilder,
490 };
491
492 #[test]
493 fn test_dbc_builder_add_messages() {
494 let version = VersionBuilder::new().version("1.0");
495 let nodes = NodesBuilder::new().add_nodes(["ECM"]);
496 let signal1 = SignalBuilder::new()
497 .name("RPM")
498 .start_bit(0)
499 .length(16)
500 .byte_order(ByteOrder::BigEndian)
501 .unsigned(true)
502 .factor(1.0)
503 .offset(0.0)
504 .min(0.0)
505 .max(100.0)
506 .receivers(ReceiversBuilder::new().none());
507 let signal2 = SignalBuilder::new()
508 .name("RPM")
509 .start_bit(0)
510 .length(16)
511 .byte_order(ByteOrder::BigEndian)
512 .unsigned(true)
513 .factor(1.0)
514 .offset(0.0)
515 .min(0.0)
516 .max(100.0)
517 .receivers(ReceiversBuilder::new().none());
518 let message1 = MessageBuilder::new()
519 .id(256)
520 .name("EngineData")
521 .dlc(8)
522 .sender("ECM")
523 .add_signal(signal1);
524 let message2 = MessageBuilder::new()
525 .id(512)
526 .name("BrakeData")
527 .dlc(4)
528 .sender("ECM")
529 .add_signal(signal2);
530
531 let dbc = DbcBuilder::new()
532 .version(version)
533 .nodes(nodes)
534 .add_messages(vec![message1, message2])
535 .build()
536 .unwrap();
537
538 assert_eq!(dbc.messages().len(), 2);
539 }
540
541 #[test]
542 fn test_dbc_builder_messages() {
543 let version = VersionBuilder::new().version("1.0");
544 let nodes = NodesBuilder::new().add_nodes(["ECM"]);
545
546 let signal1 = SignalBuilder::new()
547 .name("RPM")
548 .start_bit(0)
549 .length(16)
550 .byte_order(ByteOrder::BigEndian)
551 .unsigned(true)
552 .factor(1.0)
553 .offset(0.0)
554 .min(0.0)
555 .max(100.0)
556 .receivers(ReceiversBuilder::new().none());
557 let message1 = MessageBuilder::new()
558 .id(256)
559 .name("EngineData")
560 .dlc(8)
561 .sender("ECM")
562 .add_signal(signal1);
563
564 let signal2 = SignalBuilder::new()
565 .name("RPM")
566 .start_bit(0)
567 .length(16)
568 .byte_order(ByteOrder::BigEndian)
569 .unsigned(true)
570 .factor(1.0)
571 .offset(0.0)
572 .min(0.0)
573 .max(100.0)
574 .receivers(ReceiversBuilder::new().none());
575 let message2 = MessageBuilder::new()
576 .id(512)
577 .name("EngineData2")
578 .dlc(8)
579 .sender("ECM")
580 .add_signal(signal2);
581
582 let dbc = DbcBuilder::new()
583 .version(version)
584 .nodes(nodes)
585 .add_message(message1)
586 .add_message(message2)
587 .build()
588 .unwrap();
589
590 assert_eq!(dbc.messages().len(), 2);
591 }
592
593 #[test]
594 fn test_dbc_builder_clear_messages() {
595 let version = VersionBuilder::new().version("1.0");
596 let nodes = NodesBuilder::new().add_nodes(["ECM"]);
597 let signal = SignalBuilder::new()
598 .name("RPM")
599 .start_bit(0)
600 .length(16)
601 .byte_order(ByteOrder::BigEndian)
602 .unsigned(true)
603 .factor(1.0)
604 .offset(0.0)
605 .min(0.0)
606 .max(100.0)
607 .receivers(ReceiversBuilder::new().none());
608 let message = MessageBuilder::new()
609 .id(256)
610 .name("EngineData")
611 .dlc(8)
612 .sender("ECM")
613 .add_signal(signal);
614
615 let dbc = DbcBuilder::new()
616 .version(version)
617 .nodes(nodes)
618 .add_message(message)
619 .clear_messages()
620 .build()
621 .unwrap();
622
623 assert_eq!(dbc.messages().len(), 0);
624 }
625
626 #[test]
627 fn test_dbc_builder_from_dbc() {
628 // Parse an existing DBC
629 let dbc_content = r#"VERSION "1.0"
630
631BU_: ECM TCM
632
633BO_ 256 Engine : 8 ECM
634 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
635"#;
636 let original_dbc = Dbc::parse(dbc_content).unwrap();
637
638 // Create builder from existing DBC
639 let modified_dbc = DbcBuilder::from_dbc(&original_dbc)
640 .add_message(MessageBuilder::new().id(512).name("Brake").dlc(4).sender("TCM"))
641 .build()
642 .unwrap();
643
644 // Verify original data is preserved
645 assert_eq!(modified_dbc.version().map(|v| v.as_str()), Some("1.0"));
646 assert_eq!(modified_dbc.nodes().len(), 2);
647 assert!(modified_dbc.nodes().contains("ECM"));
648 assert!(modified_dbc.nodes().contains("TCM"));
649
650 // Verify original message is present
651 assert_eq!(modified_dbc.messages().len(), 2);
652 assert!(modified_dbc.messages().iter().any(|m| m.id() == 256));
653 assert!(modified_dbc.messages().iter().any(|m| m.id() == 512));
654
655 // Verify original message's signal is preserved
656 let engine_msg = modified_dbc.messages().iter().find(|m| m.id() == 256).unwrap();
657 assert_eq!(engine_msg.signals().len(), 1);
658 assert_eq!(engine_msg.signals().at(0).unwrap().name(), "RPM");
659 }
660
661 #[test]
662 fn test_dbc_builder_from_dbc_empty() {
663 // Parse a minimal DBC
664 let dbc_content = r#"VERSION "1.0"
665
666BU_:
667"#;
668 let original_dbc = Dbc::parse(dbc_content).unwrap();
669
670 // Create builder from existing DBC
671 let modified_dbc = DbcBuilder::from_dbc(&original_dbc)
672 .add_message(MessageBuilder::new().id(256).name("Test").dlc(8).sender("ECM"))
673 .build()
674 .unwrap();
675
676 // Verify version is preserved
677 assert_eq!(modified_dbc.version().map(|v| v.as_str()), Some("1.0"));
678 // Empty nodes are preserved
679 assert!(modified_dbc.nodes().is_empty());
680 // New message is added
681 assert_eq!(modified_dbc.messages().len(), 1);
682 }
683
684 #[test]
685 fn test_dbc_builder_from_dbc_with_extended_multiplexing() {
686 // Parse a DBC with extended multiplexing
687 let dbc_content = r#"VERSION "1.0"
688
689BU_: ECM
690
691BO_ 500 MuxMessage : 8 ECM
692 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
693 SG_ SignalA m0 : 16|16@1+ (0.1,0) [0|100] ""
694
695SG_MUL_VAL_ 500 SignalA Mux1 0-5,10-15 ;
696"#;
697 let original_dbc = Dbc::parse(dbc_content).unwrap();
698
699 // Verify original has extended multiplexing
700 assert_eq!(original_dbc.extended_multiplexing().len(), 1);
701
702 // Create builder from existing DBC and modify it
703 let modified_dbc = DbcBuilder::from_dbc(&original_dbc)
704 .add_extended_multiplexing(
705 ExtendedMultiplexingBuilder::new()
706 .message_id(500)
707 .signal_name("SignalA")
708 .multiplexer_switch("Mux1")
709 .add_value_range(20, 25),
710 )
711 .build()
712 .unwrap();
713
714 // Verify extended multiplexing is preserved and new one added
715 assert_eq!(modified_dbc.extended_multiplexing().len(), 2);
716
717 // Check original entry
718 let original_entry = &modified_dbc.extended_multiplexing()[0];
719 assert_eq!(original_entry.signal_name(), "SignalA");
720 assert_eq!(original_entry.value_ranges().len(), 2);
721 assert_eq!(original_entry.value_ranges()[0], (0, 5));
722 assert_eq!(original_entry.value_ranges()[1], (10, 15));
723
724 // Check new entry
725 let new_entry = &modified_dbc.extended_multiplexing()[1];
726 assert_eq!(new_entry.signal_name(), "SignalA");
727 assert_eq!(new_entry.value_ranges().len(), 1);
728 assert_eq!(new_entry.value_ranges()[0], (20, 25));
729 }
730}
731
732impl Default for DbcBuilder {
733 fn default() -> Self {
734 Self::new()
735 }
736}