dbc_rs/message/builder/
build.rs

1use super::MessageBuilder;
2use crate::{
3    Error, MAX_NAME_SIZE, Message, Result, Signal, SignalBuilder, compat, message::Signals,
4    required_field,
5};
6
7impl MessageBuilder {
8    #[allow(clippy::type_complexity)]
9    fn extract_fields(
10        self,
11    ) -> Result<(u32, String, u8, String, Vec<SignalBuilder>, Option<String>)> {
12        let id = required_field!(self.id, Error::message(Error::MESSAGE_ID_REQUIRED))?;
13        let name = required_field!(self.name, Error::message(Error::MESSAGE_NAME_EMPTY))?;
14        let dlc = required_field!(self.dlc, Error::message(Error::MESSAGE_DLC_REQUIRED))?;
15        let sender = required_field!(self.sender, Error::message(Error::MESSAGE_SENDER_EMPTY))?;
16        Ok((id, name, dlc, sender, self.signals, self.comment))
17    }
18
19    /// Validates the builder configuration without building the final `Message`.
20    ///
21    /// This performs full validation of all fields:
22    /// - Checks that required fields (id, name, dlc, sender) are set
23    /// - Validates message-level constraints (DLC range, name not empty)
24    /// - Builds and validates all signals (overlap detection, bounds checking)
25    ///
26    /// # Note
27    ///
28    /// This method clones and builds all signals internally for validation.
29    /// If you only need the final `Message`, call `build()` directly.
30    pub fn validate(&self) -> Result<()> {
31        // Validate required fields are present
32        let id = required_field!(self.id, Error::message(Error::MESSAGE_ID_REQUIRED))?;
33        let name = required_field!(
34            self.name.as_ref(),
35            Error::message(Error::MESSAGE_NAME_EMPTY)
36        )?;
37        let dlc = required_field!(self.dlc, Error::message(Error::MESSAGE_DLC_REQUIRED))?;
38        let sender = required_field!(
39            self.sender.as_ref(),
40            Error::message(Error::MESSAGE_SENDER_EMPTY)
41        )?;
42
43        // Build all signals for validation
44        let built_signals: Vec<Signal> = self
45            .signals
46            .iter()
47            .cloned()
48            .map(|sig_builder| sig_builder.build())
49            .collect::<Result<Vec<_>>>()?;
50
51        // Validate message with signals
52        Message::validate(id, name, dlc, sender, &built_signals)?;
53
54        Ok(())
55    }
56
57    /// Builds and validates the `Message`.
58    ///
59    /// Consumes the builder and returns a fully constructed and validated [`Message`].
60    ///
61    /// # Errors
62    ///
63    /// Returns an error if:
64    /// - Any required field (id, name, dlc, sender) is missing
65    /// - Name exceeds maximum length (32 characters)
66    /// - Any signal fails to build or validate
67    /// - Signals overlap in the message payload
68    /// - Signal extends beyond DLC bounds
69    ///
70    /// # Examples
71    ///
72    /// ```rust,no_run
73    /// use dbc_rs::{MessageBuilder, SignalBuilder, ByteOrder, ReceiversBuilder};
74    ///
75    /// let signal = SignalBuilder::new()
76    ///     .name("RPM")
77    ///     .start_bit(0)
78    ///     .length(16)
79    ///     .byte_order(ByteOrder::LittleEndian)
80    ///     .unsigned(true)
81    ///     .factor(1.0)
82    ///     .offset(0.0)
83    ///     .min(0.0)
84    ///     .max(8000.0)
85    ///     .receivers(ReceiversBuilder::new().none());
86    ///
87    /// let message = MessageBuilder::new()
88    ///     .id(0x100)
89    ///     .name("EngineData")
90    ///     .dlc(8)
91    ///     .sender("ECM")
92    ///     .add_signal(signal)
93    ///     .build()?;
94    ///
95    /// assert_eq!(message.name(), "EngineData");
96    /// # Ok::<(), dbc_rs::Error>(())
97    /// ```
98    pub fn build(self) -> Result<Message> {
99        let (id, name, dlc, sender, signals, comment) = self.extract_fields()?;
100        // Build all signals first
101        let built_signals: Vec<Signal> = signals
102            .into_iter()
103            .map(|sig_builder| sig_builder.build())
104            .collect::<Result<Vec<_>>>()?;
105        // Validate before construction
106        Message::validate(id, &name, dlc, &sender, &built_signals)?;
107
108        // Convert to owned compat types (validation passed, so these should succeed)
109        let name_str: compat::String<{ MAX_NAME_SIZE }> = compat::validate_name(&name)?;
110        let sender_str: compat::String<{ MAX_NAME_SIZE }> = compat::validate_name(&sender)?;
111        let signals_collection = Signals::from_slice(&built_signals);
112
113        Ok(Message::new(
114            id,
115            name_str,
116            dlc,
117            sender_str,
118            signals_collection,
119            comment.map(|c| c.into()),
120        ))
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::{ByteOrder, ReceiversBuilder};
128
129    fn minimal_signal() -> SignalBuilder {
130        SignalBuilder::new()
131            .name("TestSignal")
132            .start_bit(0)
133            .length(8)
134            .byte_order(ByteOrder::LittleEndian)
135            .unsigned(true)
136            .factor(1.0)
137            .offset(0.0)
138            .min(0.0)
139            .max(255.0)
140            .receivers(ReceiversBuilder::new().none())
141    }
142
143    fn minimal_message() -> MessageBuilder {
144        MessageBuilder::new().id(256).name("TestMessage").dlc(8).sender("ECM")
145    }
146
147    #[test]
148    fn test_message_builder_new() {
149        let builder = MessageBuilder::new();
150        // Default builder should fail to build
151        assert!(builder.build().is_err());
152    }
153
154    #[test]
155    fn test_message_builder_default() {
156        let builder = MessageBuilder::default();
157        assert!(builder.build().is_err());
158    }
159
160    #[test]
161    fn test_message_builder_minimal() {
162        let message = minimal_message().build().unwrap();
163
164        assert_eq!(message.id(), 256);
165        assert_eq!(message.name(), "TestMessage");
166        assert_eq!(message.dlc(), 8);
167        assert_eq!(message.sender(), "ECM");
168        assert_eq!(message.signals().len(), 0);
169    }
170
171    #[test]
172    fn test_message_builder_missing_id() {
173        let result = MessageBuilder::new().name("Test").dlc(8).sender("ECM").build();
174
175        assert!(result.is_err());
176    }
177
178    #[test]
179    fn test_message_builder_missing_name() {
180        let result = MessageBuilder::new().id(256).dlc(8).sender("ECM").build();
181
182        assert!(result.is_err());
183    }
184
185    #[test]
186    fn test_message_builder_missing_dlc() {
187        let result = MessageBuilder::new().id(256).name("Test").sender("ECM").build();
188
189        assert!(result.is_err());
190    }
191
192    #[test]
193    fn test_message_builder_missing_sender() {
194        let result = MessageBuilder::new().id(256).name("Test").dlc(8).build();
195
196        assert!(result.is_err());
197    }
198
199    #[test]
200    fn test_message_builder_validate_valid() {
201        let builder = minimal_message().add_signal(minimal_signal());
202        let result = builder.validate();
203        assert!(result.is_ok());
204    }
205
206    #[test]
207    fn test_message_builder_validate_missing_id() {
208        let builder = MessageBuilder::new().name("Test").dlc(8).sender("ECM");
209        let result = builder.validate();
210        assert!(result.is_err());
211    }
212
213    #[test]
214    fn test_message_builder_validate_missing_name() {
215        let builder = MessageBuilder::new().id(256).dlc(8).sender("ECM");
216        let result = builder.validate();
217        assert!(result.is_err());
218    }
219
220    #[test]
221    fn test_message_builder_validate_missing_dlc() {
222        let builder = MessageBuilder::new().id(256).name("Test").sender("ECM");
223        let result = builder.validate();
224        assert!(result.is_err());
225    }
226
227    #[test]
228    fn test_message_builder_validate_missing_sender() {
229        let builder = MessageBuilder::new().id(256).name("Test").dlc(8);
230        let result = builder.validate();
231        assert!(result.is_err());
232    }
233
234    #[test]
235    fn test_message_builder_extended_id() {
236        // Extended CAN ID (29-bit, with flag)
237        let message = MessageBuilder::new()
238            .id(0x80000100) // Extended ID flag + ID
239            .name("ExtendedMsg")
240            .dlc(8)
241            .sender("ECM")
242            .build()
243            .unwrap();
244
245        // id() returns the raw CAN ID without the extended flag
246        assert_eq!(message.id(), 0x100);
247        assert!(message.is_extended());
248    }
249
250    #[test]
251    fn test_message_builder_dlc_zero() {
252        let message = minimal_message().dlc(0).build().unwrap();
253        assert_eq!(message.dlc(), 0);
254    }
255
256    #[test]
257    fn test_message_builder_dlc_max() {
258        // CAN FD supports up to 64 bytes
259        let message = minimal_message().dlc(64).build().unwrap();
260        assert_eq!(message.dlc(), 64);
261    }
262
263    #[test]
264    fn test_message_builder_with_comment() {
265        let message = minimal_message()
266            .comment("Engine status message with RPM and temperature")
267            .build()
268            .unwrap();
269
270        assert_eq!(
271            message.comment(),
272            Some("Engine status message with RPM and temperature")
273        );
274    }
275
276    #[test]
277    fn test_message_builder_without_comment() {
278        let message = minimal_message().build().unwrap();
279        assert_eq!(message.comment(), None);
280    }
281}