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