dbc_rs/dbc/builder/
build.rs

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