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