dbc_rs/dbc/builder/
build.rs

1use super::DbcBuilder;
2use crate::{
3    Dbc, ExtendedMultiplexing, MAX_EXTENDED_MULTIPLEXING, MAX_MESSAGES, MAX_NAME_SIZE, Message,
4    Nodes, Result, Version,
5    compat::{BTreeMap, String, Vec as CompatVec},
6    dbc::{Messages, Validate},
7};
8
9impl DbcBuilder {
10    /// Validates the builder without constructing the `Dbc`.
11    ///
12    /// This method performs all validation checks. Note that this consumes
13    /// the builder. If you want to keep the builder after validation, call
14    /// `build()` instead and check the result.
15    ///
16    /// # Examples
17    ///
18    /// ```rust,no_run
19    /// use dbc_rs::DbcBuilder;
20    ///
21    /// let builder = DbcBuilder::new();
22    /// if builder.validate().is_err() {
23    ///     // Handle validation error
24    /// }
25    /// ```
26    #[must_use = "validation result should be checked"]
27    pub fn validate(self) -> Result<()> {
28        // Build and validate (extract_fields builds everything)
29        // We need to call extract_fields from the impl<'a> block
30        // Since validate doesn't need the lifetime, we can just build and drop
31        let (_version, nodes, messages, value_descriptions, extended_multiplexing) = {
32            let version = self.version.build()?;
33            let nodes = self.nodes.build()?;
34            let messages: std::vec::Vec<Message> = self
35                .messages
36                .into_iter()
37                .map(|builder| builder.build())
38                .collect::<Result<std::vec::Vec<_>>>()?;
39            let mut value_descriptions_map: BTreeMap<
40                (Option<u32>, String<{ MAX_NAME_SIZE }>),
41                crate::value_descriptions::ValueDescriptions,
42                { MAX_MESSAGES },
43            > = BTreeMap::new();
44            for ((message_id, signal_name), vd_builder) in self.value_descriptions {
45                let vd: crate::value_descriptions::ValueDescriptions = vd_builder.build()?;
46                let compat_signal_name = String::try_from(signal_name.as_str())
47                    .map_err(|_| crate::Error::Validation(crate::Error::MAX_NAME_SIZE_EXCEEDED))?;
48                let _ = value_descriptions_map.insert((message_id, compat_signal_name), vd);
49            }
50            let value_descriptions = crate::dbc::ValueDescriptionsMap::new(value_descriptions_map);
51
52            // Build extended multiplexing entries
53            let extended_multiplexing_vec: std::vec::Vec<ExtendedMultiplexing> = self
54                .extended_multiplexing
55                .into_iter()
56                .map(|builder| builder.build())
57                .collect::<Result<std::vec::Vec<_>>>()?;
58
59            (
60                version,
61                nodes,
62                messages,
63                value_descriptions,
64                extended_multiplexing_vec,
65            )
66        };
67
68        // Validate messages and extended multiplexing
69        Validate::validate(
70            &nodes,
71            &messages,
72            Some(&value_descriptions),
73            Some(&extended_multiplexing),
74        )?;
75
76        Ok(())
77    }
78
79    fn extract_fields(
80        self,
81    ) -> Result<(
82        Version,
83        Nodes,
84        Messages,
85        crate::dbc::ValueDescriptionsMap,
86        CompatVec<ExtendedMultiplexing, { MAX_EXTENDED_MULTIPLEXING }>,
87    )> {
88        // Build version
89        let version = self.version.build()?;
90
91        // Build nodes (allow empty - DBC spec allows empty BU_: line)
92        let nodes = self.nodes.build()?;
93
94        // Build messages
95        // Collect into a temporary Vec first, then convert to slice for Messages::new
96        let messages_vec: std::vec::Vec<Message> = self
97            .messages
98            .into_iter()
99            .map(|builder| builder.build())
100            .collect::<Result<std::vec::Vec<_>>>()?;
101        let messages = Messages::new(&messages_vec)?;
102
103        // Build value descriptions
104        let mut value_descriptions_map: BTreeMap<
105            (Option<u32>, String<{ MAX_NAME_SIZE }>),
106            crate::value_descriptions::ValueDescriptions,
107            { MAX_MESSAGES },
108        > = BTreeMap::new();
109        for ((message_id, signal_name), vd_builder) in self.value_descriptions {
110            let vd: crate::value_descriptions::ValueDescriptions = vd_builder.build()?;
111            let compat_signal_name = String::try_from(signal_name.as_str())
112                .map_err(|_| crate::Error::Validation(crate::Error::MAX_NAME_SIZE_EXCEEDED))?;
113            let _ = value_descriptions_map.insert((message_id, compat_signal_name), vd);
114        }
115        let value_descriptions = crate::dbc::ValueDescriptionsMap::new(value_descriptions_map);
116
117        // Build extended multiplexing entries
118        let extended_multiplexing_vec: std::vec::Vec<ExtendedMultiplexing> = self
119            .extended_multiplexing
120            .into_iter()
121            .map(|builder| builder.build())
122            .collect::<Result<std::vec::Vec<_>>>()?;
123
124        // Convert to compat Vec
125        let mut extended_multiplexing: CompatVec<
126            ExtendedMultiplexing,
127            { MAX_EXTENDED_MULTIPLEXING },
128        > = CompatVec::new();
129        for ext_mux in extended_multiplexing_vec {
130            extended_multiplexing
131                .push(ext_mux)
132                .map_err(|_| crate::Error::expected("too many extended multiplexing entries"))?;
133        }
134
135        Ok((
136            version,
137            nodes,
138            messages,
139            value_descriptions,
140            extended_multiplexing,
141        ))
142    }
143
144    /// Builds the `Dbc` from the builder.
145    ///
146    /// This method validates all fields and constructs the `Dbc` instance.
147    /// Returns an error if validation fails.
148    ///
149    /// # Examples
150    ///
151    /// ```rust,no_run
152    /// use dbc_rs::{DbcBuilder, VersionBuilder, NodesBuilder};
153    ///
154    /// let dbc = DbcBuilder::new()
155    ///     .version(VersionBuilder::new().version("1.0"))
156    ///     .nodes(NodesBuilder::new().add_node("ECM"))
157    ///     .build()?;
158    /// # Ok::<(), dbc_rs::Error>(())
159    /// ```
160    pub fn build(self) -> Result<Dbc> {
161        let (version, nodes, messages, value_descriptions, extended_multiplexing) =
162            self.extract_fields()?;
163        // Validate before construction
164        // Get slice from Messages for validation
165        let messages_slice: std::vec::Vec<Message> = messages.iter().cloned().collect();
166        let extended_multiplexing_slice: std::vec::Vec<ExtendedMultiplexing> =
167            extended_multiplexing.iter().cloned().collect();
168        Validate::validate(
169            &nodes,
170            &messages_slice,
171            Some(&value_descriptions),
172            Some(&extended_multiplexing_slice),
173        )?;
174        Ok(Dbc::new(
175            Some(version),
176            nodes,
177            messages,
178            value_descriptions,
179            extended_multiplexing,
180        ))
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    #![allow(clippy::float_cmp)]
187    use super::DbcBuilder;
188    use crate::{
189        ByteOrder, ExtendedMultiplexingBuilder, MessageBuilder, NodesBuilder, ReceiversBuilder,
190        SignalBuilder, ValueDescriptionsBuilder, VersionBuilder,
191    };
192
193    #[test]
194    fn test_dbc_builder_valid() {
195        let version = VersionBuilder::new().version("1.0");
196        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
197        let signal = SignalBuilder::new()
198            .name("RPM")
199            .start_bit(0)
200            .length(16)
201            .byte_order(ByteOrder::BigEndian)
202            .unsigned(true)
203            .factor(1.0)
204            .offset(0.0)
205            .min(0.0)
206            .max(100.0)
207            .receivers(ReceiversBuilder::new().none());
208        let message = MessageBuilder::new()
209            .id(256)
210            .name("EngineData")
211            .dlc(8)
212            .sender("ECM")
213            .add_signal(signal);
214
215        let dbc = DbcBuilder::new()
216            .version(version)
217            .nodes(nodes)
218            .add_message(message)
219            .build()
220            .unwrap();
221
222        assert_eq!(dbc.messages().len(), 1);
223        assert_eq!(dbc.messages().at(0).unwrap().id(), 256);
224    }
225
226    #[test]
227    fn test_dbc_builder_missing_version() {
228        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
229        let signal = SignalBuilder::new()
230            .name("RPM")
231            .start_bit(0)
232            .length(16)
233            .byte_order(ByteOrder::BigEndian)
234            .unsigned(true)
235            .factor(1.0)
236            .offset(0.0)
237            .min(0.0)
238            .max(100.0)
239            .receivers(ReceiversBuilder::new().none());
240        let message = MessageBuilder::new()
241            .id(256)
242            .name("EngineData")
243            .dlc(8)
244            .sender("ECM")
245            .add_signal(signal);
246
247        let result = DbcBuilder::new().nodes(nodes).add_message(message).build();
248        // VersionBuilder now allows empty version, so this should succeed
249        assert!(result.is_ok());
250        let dbc = result.unwrap();
251        assert_eq!(dbc.version().unwrap().as_str(), "");
252    }
253
254    #[test]
255    fn test_dbc_builder_missing_nodes() {
256        // Empty nodes are now allowed per DBC spec
257        // When nodes are empty, sender validation is skipped
258        let version = VersionBuilder::new().version("1.0");
259        let signal = SignalBuilder::new()
260            .name("RPM")
261            .start_bit(0)
262            .length(16)
263            .byte_order(ByteOrder::BigEndian)
264            .unsigned(true)
265            .factor(1.0)
266            .offset(0.0)
267            .min(0.0)
268            .max(100.0)
269            .receivers(ReceiversBuilder::new().none());
270        let message = MessageBuilder::new()
271            .id(256)
272            .name("EngineData")
273            .dlc(8)
274            .sender("ECM")
275            .add_signal(signal);
276
277        // Building without nodes should succeed (empty nodes allowed)
278        let result = DbcBuilder::new().version(version).add_message(message).build();
279        assert!(result.is_ok());
280        let dbc = result.unwrap();
281        assert!(dbc.nodes().is_empty());
282    }
283
284    #[test]
285    fn test_dbc_builder_validate_missing_version() {
286        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
287        // VersionBuilder now allows empty version, so validation should succeed
288        let result = DbcBuilder::new().nodes(nodes).validate();
289        assert!(result.is_ok());
290    }
291
292    #[test]
293    fn test_dbc_builder_validate_missing_nodes() {
294        // Empty nodes are now allowed per DBC spec
295        let version = VersionBuilder::new().version("1.0");
296        let result = DbcBuilder::new().version(version).validate();
297        // Validation should succeed with empty nodes
298        assert!(result.is_ok());
299    }
300
301    #[test]
302    fn test_dbc_builder_validate_valid() {
303        let version = VersionBuilder::new().version("1.0");
304        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
305        let signal = SignalBuilder::new()
306            .name("RPM")
307            .start_bit(0)
308            .length(16)
309            .byte_order(ByteOrder::BigEndian)
310            .unsigned(true)
311            .factor(1.0)
312            .offset(0.0)
313            .min(0.0)
314            .max(100.0)
315            .receivers(ReceiversBuilder::new().none());
316        let message = MessageBuilder::new()
317            .id(256)
318            .name("EngineData")
319            .dlc(8)
320            .sender("ECM")
321            .add_signal(signal);
322
323        // validate() consumes the builder, so we can't use it after
324        // But we can check it doesn't error
325        let builder = DbcBuilder::new().version(version).nodes(nodes).add_message(message);
326        let result = builder.validate();
327        assert!(result.is_ok());
328    }
329
330    // ========================================================================
331    // Value Descriptions Tests
332    // ========================================================================
333
334    #[test]
335    fn test_dbc_builder_with_value_descriptions() {
336        let version = VersionBuilder::new().version("1.0");
337        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
338        let signal = SignalBuilder::new()
339            .name("Gear")
340            .start_bit(0)
341            .length(8)
342            .byte_order(ByteOrder::LittleEndian)
343            .unsigned(true)
344            .factor(1.0)
345            .offset(0.0)
346            .min(0.0)
347            .max(5.0)
348            .receivers(ReceiversBuilder::new().none());
349        let message = MessageBuilder::new()
350            .id(256)
351            .name("Transmission")
352            .dlc(8)
353            .sender("ECM")
354            .add_signal(signal);
355
356        let value_desc = ValueDescriptionsBuilder::new()
357            .add_entry(0, "Park")
358            .add_entry(1, "Reverse")
359            .add_entry(2, "Neutral")
360            .add_entry(3, "Drive");
361
362        let dbc = DbcBuilder::new()
363            .version(version)
364            .nodes(nodes)
365            .add_message(message)
366            .add_value_description(256, "Gear", value_desc)
367            .build()
368            .unwrap();
369
370        // Verify value descriptions are present
371        let vd = dbc.value_descriptions_for_signal(256, "Gear").unwrap();
372        assert_eq!(vd.get(0), Some("Park"));
373        assert_eq!(vd.get(1), Some("Reverse"));
374        assert_eq!(vd.get(2), Some("Neutral"));
375        assert_eq!(vd.get(3), Some("Drive"));
376    }
377
378    #[test]
379    fn test_dbc_builder_value_descriptions_message_not_found() {
380        let version = VersionBuilder::new().version("1.0");
381        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
382        let signal = SignalBuilder::new()
383            .name("RPM")
384            .start_bit(0)
385            .length(16)
386            .byte_order(ByteOrder::BigEndian)
387            .unsigned(true)
388            .factor(1.0)
389            .offset(0.0)
390            .min(0.0)
391            .max(100.0)
392            .receivers(ReceiversBuilder::new().none());
393        let message = MessageBuilder::new()
394            .id(256)
395            .name("EngineData")
396            .dlc(8)
397            .sender("ECM")
398            .add_signal(signal);
399
400        // Add value description for non-existent message
401        let value_desc = ValueDescriptionsBuilder::new().add_entry(0, "Off").add_entry(1, "On");
402
403        let result = DbcBuilder::new()
404            .version(version)
405            .nodes(nodes)
406            .add_message(message)
407            .add_value_description(999, "RPM", value_desc) // Message 999 doesn't exist
408            .build();
409
410        assert!(result.is_err());
411    }
412
413    #[test]
414    fn test_dbc_builder_value_descriptions_signal_not_found() {
415        let version = VersionBuilder::new().version("1.0");
416        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
417        let signal = SignalBuilder::new()
418            .name("RPM")
419            .start_bit(0)
420            .length(16)
421            .byte_order(ByteOrder::BigEndian)
422            .unsigned(true)
423            .factor(1.0)
424            .offset(0.0)
425            .min(0.0)
426            .max(100.0)
427            .receivers(ReceiversBuilder::new().none());
428        let message = MessageBuilder::new()
429            .id(256)
430            .name("EngineData")
431            .dlc(8)
432            .sender("ECM")
433            .add_signal(signal);
434
435        // Add value description for non-existent signal
436        let value_desc = ValueDescriptionsBuilder::new().add_entry(0, "Off").add_entry(1, "On");
437
438        let result = DbcBuilder::new()
439            .version(version)
440            .nodes(nodes)
441            .add_message(message)
442            .add_value_description(256, "NonExistentSignal", value_desc)
443            .build();
444
445        assert!(result.is_err());
446    }
447
448    #[test]
449    fn test_dbc_builder_validate_with_value_descriptions() {
450        let version = VersionBuilder::new().version("1.0");
451        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
452        let signal = SignalBuilder::new()
453            .name("Gear")
454            .start_bit(0)
455            .length(8)
456            .byte_order(ByteOrder::LittleEndian)
457            .unsigned(true)
458            .factor(1.0)
459            .offset(0.0)
460            .min(0.0)
461            .max(5.0)
462            .receivers(ReceiversBuilder::new().none());
463        let message = MessageBuilder::new()
464            .id(256)
465            .name("Transmission")
466            .dlc(8)
467            .sender("ECM")
468            .add_signal(signal);
469
470        let value_desc = ValueDescriptionsBuilder::new().add_entry(0, "Park").add_entry(1, "Drive");
471
472        let result = DbcBuilder::new()
473            .version(version)
474            .nodes(nodes)
475            .add_message(message)
476            .add_value_description(256, "Gear", value_desc)
477            .validate();
478
479        assert!(result.is_ok());
480    }
481
482    // ========================================================================
483    // Extended Multiplexing Tests
484    // ========================================================================
485
486    #[test]
487    fn test_dbc_builder_with_extended_multiplexing() {
488        let version = VersionBuilder::new().version("1.0");
489        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
490
491        // Create a multiplexer signal
492        let mux_signal = SignalBuilder::new()
493            .name("Mux1")
494            .start_bit(0)
495            .length(8)
496            .byte_order(ByteOrder::LittleEndian)
497            .unsigned(true)
498            .factor(1.0)
499            .offset(0.0)
500            .min(0.0)
501            .max(255.0)
502            .receivers(ReceiversBuilder::new().none());
503
504        // Create a multiplexed signal
505        let signal_a = SignalBuilder::new()
506            .name("SignalA")
507            .start_bit(16)
508            .length(16)
509            .byte_order(ByteOrder::LittleEndian)
510            .unsigned(true)
511            .factor(0.1)
512            .offset(0.0)
513            .min(0.0)
514            .max(100.0)
515            .receivers(ReceiversBuilder::new().none());
516
517        let message = MessageBuilder::new()
518            .id(500)
519            .name("MuxMessage")
520            .dlc(8)
521            .sender("ECM")
522            .add_signal(mux_signal)
523            .add_signal(signal_a);
524
525        // Create extended multiplexing entry
526        let ext_mux = ExtendedMultiplexingBuilder::new()
527            .message_id(500)
528            .signal_name("SignalA")
529            .multiplexer_switch("Mux1")
530            .add_value_range(0, 5)
531            .add_value_range(10, 15);
532
533        let dbc = DbcBuilder::new()
534            .version(version)
535            .nodes(nodes)
536            .add_message(message)
537            .add_extended_multiplexing(ext_mux)
538            .build()
539            .unwrap();
540
541        assert_eq!(dbc.messages().len(), 1);
542        assert_eq!(dbc.extended_multiplexing().len(), 1);
543
544        let ext_mux = &dbc.extended_multiplexing()[0];
545        assert_eq!(ext_mux.message_id(), 500);
546        assert_eq!(ext_mux.signal_name(), "SignalA");
547        assert_eq!(ext_mux.multiplexer_switch(), "Mux1");
548        assert_eq!(ext_mux.value_ranges().len(), 2);
549        assert_eq!(ext_mux.value_ranges()[0], (0, 5));
550        assert_eq!(ext_mux.value_ranges()[1], (10, 15));
551    }
552
553    #[test]
554    fn test_dbc_builder_extended_multiplexing_message_not_found() {
555        let version = VersionBuilder::new().version("1.0");
556        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
557
558        let signal = SignalBuilder::new()
559            .name("RPM")
560            .start_bit(0)
561            .length(16)
562            .byte_order(ByteOrder::BigEndian)
563            .unsigned(true)
564            .factor(1.0)
565            .offset(0.0)
566            .min(0.0)
567            .max(100.0)
568            .receivers(ReceiversBuilder::new().none());
569
570        let message = MessageBuilder::new()
571            .id(256)
572            .name("EngineData")
573            .dlc(8)
574            .sender("ECM")
575            .add_signal(signal);
576
577        // Create extended multiplexing referencing non-existent message
578        let ext_mux = ExtendedMultiplexingBuilder::new()
579            .message_id(999) // Non-existent message
580            .signal_name("RPM")
581            .multiplexer_switch("Mux1")
582            .add_value_range(0, 5);
583
584        let result = DbcBuilder::new()
585            .version(version)
586            .nodes(nodes)
587            .add_message(message)
588            .add_extended_multiplexing(ext_mux)
589            .build();
590
591        assert!(result.is_err());
592    }
593
594    #[test]
595    fn test_dbc_builder_extended_multiplexing_signal_not_found() {
596        let version = VersionBuilder::new().version("1.0");
597        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
598
599        let mux_signal = SignalBuilder::new()
600            .name("Mux1")
601            .start_bit(0)
602            .length(8)
603            .byte_order(ByteOrder::BigEndian)
604            .unsigned(true)
605            .factor(1.0)
606            .offset(0.0)
607            .min(0.0)
608            .max(100.0)
609            .receivers(ReceiversBuilder::new().none());
610
611        let message = MessageBuilder::new()
612            .id(256)
613            .name("EngineData")
614            .dlc(8)
615            .sender("ECM")
616            .add_signal(mux_signal);
617
618        // Create extended multiplexing referencing non-existent signal
619        let ext_mux = ExtendedMultiplexingBuilder::new()
620            .message_id(256)
621            .signal_name("NonExistent") // Non-existent signal
622            .multiplexer_switch("Mux1")
623            .add_value_range(0, 5);
624
625        let result = DbcBuilder::new()
626            .version(version)
627            .nodes(nodes)
628            .add_message(message)
629            .add_extended_multiplexing(ext_mux)
630            .build();
631
632        assert!(result.is_err());
633    }
634
635    #[test]
636    fn test_dbc_builder_extended_multiplexing_switch_not_found() {
637        let version = VersionBuilder::new().version("1.0");
638        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
639
640        let signal = SignalBuilder::new()
641            .name("SignalA")
642            .start_bit(0)
643            .length(16)
644            .byte_order(ByteOrder::BigEndian)
645            .unsigned(true)
646            .factor(1.0)
647            .offset(0.0)
648            .min(0.0)
649            .max(100.0)
650            .receivers(ReceiversBuilder::new().none());
651
652        let message = MessageBuilder::new()
653            .id(256)
654            .name("EngineData")
655            .dlc(8)
656            .sender("ECM")
657            .add_signal(signal);
658
659        // Create extended multiplexing referencing non-existent multiplexer switch
660        let ext_mux = ExtendedMultiplexingBuilder::new()
661            .message_id(256)
662            .signal_name("SignalA")
663            .multiplexer_switch("NonExistentMux") // Non-existent switch
664            .add_value_range(0, 5);
665
666        let result = DbcBuilder::new()
667            .version(version)
668            .nodes(nodes)
669            .add_message(message)
670            .add_extended_multiplexing(ext_mux)
671            .build();
672
673        assert!(result.is_err());
674    }
675
676    #[test]
677    fn test_dbc_builder_extended_multiplexing_invalid_range() {
678        let version = VersionBuilder::new().version("1.0");
679        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
680
681        let mux_signal = SignalBuilder::new()
682            .name("Mux1")
683            .start_bit(0)
684            .length(8)
685            .byte_order(ByteOrder::LittleEndian)
686            .unsigned(true)
687            .factor(1.0)
688            .offset(0.0)
689            .min(0.0)
690            .max(255.0)
691            .receivers(ReceiversBuilder::new().none());
692
693        let signal_a = SignalBuilder::new()
694            .name("SignalA")
695            .start_bit(16)
696            .length(16)
697            .byte_order(ByteOrder::LittleEndian)
698            .unsigned(true)
699            .factor(0.1)
700            .offset(0.0)
701            .min(0.0)
702            .max(100.0)
703            .receivers(ReceiversBuilder::new().none());
704
705        let message = MessageBuilder::new()
706            .id(500)
707            .name("MuxMessage")
708            .dlc(8)
709            .sender("ECM")
710            .add_signal(mux_signal)
711            .add_signal(signal_a);
712
713        // Create extended multiplexing with invalid range (min > max)
714        let ext_mux = ExtendedMultiplexingBuilder::new()
715            .message_id(500)
716            .signal_name("SignalA")
717            .multiplexer_switch("Mux1")
718            .add_value_range(10, 5); // Invalid: min > max
719
720        let result = DbcBuilder::new()
721            .version(version)
722            .nodes(nodes)
723            .add_message(message)
724            .add_extended_multiplexing(ext_mux)
725            .build();
726
727        assert!(result.is_err());
728    }
729
730    #[test]
731    fn test_dbc_builder_validate_with_extended_multiplexing() {
732        let version = VersionBuilder::new().version("1.0");
733        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
734
735        let mux_signal = SignalBuilder::new()
736            .name("Mux1")
737            .start_bit(0)
738            .length(8)
739            .byte_order(ByteOrder::LittleEndian)
740            .unsigned(true)
741            .factor(1.0)
742            .offset(0.0)
743            .min(0.0)
744            .max(255.0)
745            .receivers(ReceiversBuilder::new().none());
746
747        let signal_a = SignalBuilder::new()
748            .name("SignalA")
749            .start_bit(16)
750            .length(16)
751            .byte_order(ByteOrder::LittleEndian)
752            .unsigned(true)
753            .factor(0.1)
754            .offset(0.0)
755            .min(0.0)
756            .max(100.0)
757            .receivers(ReceiversBuilder::new().none());
758
759        let message = MessageBuilder::new()
760            .id(500)
761            .name("MuxMessage")
762            .dlc(8)
763            .sender("ECM")
764            .add_signal(mux_signal)
765            .add_signal(signal_a);
766
767        let ext_mux = ExtendedMultiplexingBuilder::new()
768            .message_id(500)
769            .signal_name("SignalA")
770            .multiplexer_switch("Mux1")
771            .add_value_range(0, 5);
772
773        let result = DbcBuilder::new()
774            .version(version)
775            .nodes(nodes)
776            .add_message(message)
777            .add_extended_multiplexing(ext_mux)
778            .validate();
779
780        assert!(result.is_ok());
781    }
782
783    #[test]
784    fn test_dbc_builder_multiple_extended_multiplexing() {
785        let version = VersionBuilder::new().version("1.0");
786        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
787
788        let mux_signal = SignalBuilder::new()
789            .name("Mux1")
790            .start_bit(0)
791            .length(8)
792            .byte_order(ByteOrder::LittleEndian)
793            .unsigned(true)
794            .factor(1.0)
795            .offset(0.0)
796            .min(0.0)
797            .max(255.0)
798            .receivers(ReceiversBuilder::new().none());
799
800        let signal_a = SignalBuilder::new()
801            .name("SignalA")
802            .start_bit(16)
803            .length(8)
804            .byte_order(ByteOrder::LittleEndian)
805            .unsigned(true)
806            .factor(1.0)
807            .offset(0.0)
808            .min(0.0)
809            .max(255.0)
810            .receivers(ReceiversBuilder::new().none());
811
812        let signal_b = SignalBuilder::new()
813            .name("SignalB")
814            .start_bit(24)
815            .length(8)
816            .byte_order(ByteOrder::LittleEndian)
817            .unsigned(true)
818            .factor(1.0)
819            .offset(0.0)
820            .min(0.0)
821            .max(255.0)
822            .receivers(ReceiversBuilder::new().none());
823
824        let message = MessageBuilder::new()
825            .id(500)
826            .name("MuxMessage")
827            .dlc(8)
828            .sender("ECM")
829            .add_signal(mux_signal)
830            .add_signal(signal_a)
831            .add_signal(signal_b);
832
833        let ext_mux_a = ExtendedMultiplexingBuilder::new()
834            .message_id(500)
835            .signal_name("SignalA")
836            .multiplexer_switch("Mux1")
837            .add_value_range(0, 5);
838
839        let ext_mux_b = ExtendedMultiplexingBuilder::new()
840            .message_id(500)
841            .signal_name("SignalB")
842            .multiplexer_switch("Mux1")
843            .add_value_range(10, 15);
844
845        let dbc = DbcBuilder::new()
846            .version(version)
847            .nodes(nodes)
848            .add_message(message)
849            .add_extended_multiplexings(vec![ext_mux_a, ext_mux_b])
850            .build()
851            .unwrap();
852
853        assert_eq!(dbc.extended_multiplexing().len(), 2);
854        assert_eq!(dbc.extended_multiplexing()[0].signal_name(), "SignalA");
855        assert_eq!(dbc.extended_multiplexing()[1].signal_name(), "SignalB");
856    }
857
858    #[test]
859    fn test_dbc_builder_clear_extended_multiplexing() {
860        let version = VersionBuilder::new().version("1.0");
861        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
862
863        let mux_signal = SignalBuilder::new()
864            .name("Mux1")
865            .start_bit(0)
866            .length(8)
867            .byte_order(ByteOrder::LittleEndian)
868            .unsigned(true)
869            .factor(1.0)
870            .offset(0.0)
871            .min(0.0)
872            .max(255.0)
873            .receivers(ReceiversBuilder::new().none());
874
875        let signal_a = SignalBuilder::new()
876            .name("SignalA")
877            .start_bit(16)
878            .length(16)
879            .byte_order(ByteOrder::LittleEndian)
880            .unsigned(true)
881            .factor(0.1)
882            .offset(0.0)
883            .min(0.0)
884            .max(100.0)
885            .receivers(ReceiversBuilder::new().none());
886
887        let message = MessageBuilder::new()
888            .id(500)
889            .name("MuxMessage")
890            .dlc(8)
891            .sender("ECM")
892            .add_signal(mux_signal)
893            .add_signal(signal_a);
894
895        let ext_mux = ExtendedMultiplexingBuilder::new()
896            .message_id(500)
897            .signal_name("SignalA")
898            .multiplexer_switch("Mux1")
899            .add_value_range(0, 5);
900
901        let dbc = DbcBuilder::new()
902            .version(version)
903            .nodes(nodes)
904            .add_message(message)
905            .add_extended_multiplexing(ext_mux)
906            .clear_extended_multiplexing()
907            .build()
908            .unwrap();
909
910        assert_eq!(dbc.extended_multiplexing().len(), 0);
911    }
912
913    // ========================================================================
914    // Combined Tests (Value Descriptions + Extended Multiplexing)
915    // ========================================================================
916
917    #[test]
918    fn test_dbc_builder_with_value_descriptions_and_extended_multiplexing() {
919        let version = VersionBuilder::new().version("1.0");
920        let nodes = NodesBuilder::new().add_nodes(["ECM"]);
921
922        let mux_signal = SignalBuilder::new()
923            .name("Mux1")
924            .start_bit(0)
925            .length(8)
926            .byte_order(ByteOrder::LittleEndian)
927            .unsigned(true)
928            .factor(1.0)
929            .offset(0.0)
930            .min(0.0)
931            .max(255.0)
932            .receivers(ReceiversBuilder::new().none());
933
934        let signal_a = SignalBuilder::new()
935            .name("SignalA")
936            .start_bit(16)
937            .length(8)
938            .byte_order(ByteOrder::LittleEndian)
939            .unsigned(true)
940            .factor(1.0)
941            .offset(0.0)
942            .min(0.0)
943            .max(3.0)
944            .receivers(ReceiversBuilder::new().none());
945
946        let message = MessageBuilder::new()
947            .id(500)
948            .name("MuxMessage")
949            .dlc(8)
950            .sender("ECM")
951            .add_signal(mux_signal)
952            .add_signal(signal_a);
953
954        let value_desc = ValueDescriptionsBuilder::new()
955            .add_entry(0, "Off")
956            .add_entry(1, "Low")
957            .add_entry(2, "Medium")
958            .add_entry(3, "High");
959
960        let ext_mux = ExtendedMultiplexingBuilder::new()
961            .message_id(500)
962            .signal_name("SignalA")
963            .multiplexer_switch("Mux1")
964            .add_value_range(0, 5);
965
966        let dbc = DbcBuilder::new()
967            .version(version)
968            .nodes(nodes)
969            .add_message(message)
970            .add_value_description(500, "SignalA", value_desc)
971            .add_extended_multiplexing(ext_mux)
972            .build()
973            .unwrap();
974
975        // Verify value descriptions
976        let vd = dbc.value_descriptions_for_signal(500, "SignalA").unwrap();
977        assert_eq!(vd.get(0), Some("Off"));
978        assert_eq!(vd.get(3), Some("High"));
979
980        // Verify extended multiplexing
981        assert_eq!(dbc.extended_multiplexing().len(), 1);
982        assert_eq!(dbc.extended_multiplexing()[0].signal_name(), "SignalA");
983    }
984}