dbc_rs/signal/builder/
build.rs

1use super::SignalBuilder;
2use crate::{ByteOrder, Error, ReceiversBuilder, Result, Signal, required_field};
3
4type SignalFields = (
5    String,
6    u16,
7    u16,
8    ByteOrder,
9    bool,
10    f64,
11    f64,
12    f64,
13    f64,
14    Option<String>,
15    ReceiversBuilder,
16    Option<String>,
17);
18
19impl SignalBuilder {
20    fn extract_fields(&self) -> Result<SignalFields> {
21        let name = required_field!(self.name.clone(), Error::signal(Error::SIGNAL_NAME_EMPTY))?;
22        let start_bit = required_field!(
23            self.start_bit,
24            Error::signal(Error::SIGNAL_START_BIT_REQUIRED)
25        )?;
26        let length = required_field!(self.length, Error::signal(Error::SIGNAL_LENGTH_REQUIRED))?;
27        let byte_order = required_field!(
28            self.byte_order,
29            Error::signal(Error::SIGNAL_BYTE_ORDER_REQUIRED)
30        )?;
31        let unsigned = required_field!(
32            self.unsigned,
33            Error::signal(Error::SIGNAL_UNSIGNED_REQUIRED)
34        )?;
35        let factor = required_field!(self.factor, Error::signal(Error::SIGNAL_FACTOR_REQUIRED))?;
36        let offset = required_field!(self.offset, Error::signal(Error::SIGNAL_OFFSET_REQUIRED))?;
37        let min = required_field!(self.min, Error::signal(Error::SIGNAL_MIN_REQUIRED))?;
38        let max = required_field!(self.max, Error::signal(Error::SIGNAL_MAX_REQUIRED))?;
39        Ok((
40            name,
41            start_bit,
42            length,
43            byte_order,
44            unsigned,
45            factor,
46            offset,
47            min,
48            max,
49            self.unit.clone(),
50            self.receivers.clone(),
51            self.comment.clone(),
52        ))
53    }
54
55    #[must_use = "validation result should be checked"]
56    pub fn validate(self) -> Result<Self> {
57        let (
58            name,
59            start_bit,
60            length,
61            byte_order,
62            unsigned,
63            factor,
64            offset,
65            min,
66            max,
67            unit,
68            receivers,
69            comment,
70        ) = self.extract_fields()?;
71
72        // Validate start_bit: must be between 0 and 511 (CAN FD maximum is 512 bits)
73        if start_bit > 511 {
74            return Err(Error::signal(Error::SIGNAL_PARSE_INVALID_START_BIT));
75        }
76
77        // Validate that start_bit + length doesn't exceed CAN FD maximum (512 bits)
78        // Note: This is a basic sanity check. Full validation (including name, min/max,
79        // message DLC bounds, and overlap detection) happens in build() when the signal
80        // is actually constructed, to avoid duplicate validation calls.
81        let end_bit = start_bit + length - 1; // -1 because length includes the start bit
82        if end_bit >= 512 {
83            return Err(Error::signal(Error::SIGNAL_EXTENDS_BEYOND_MESSAGE));
84        }
85        Ok(Self {
86            name: Some(name),
87            start_bit: Some(start_bit),
88            length: Some(length),
89            byte_order: Some(byte_order),
90            unsigned: Some(unsigned),
91            factor: Some(factor),
92            offset: Some(offset),
93            min: Some(min),
94            max: Some(max),
95            unit,
96            receivers,
97            comment,
98        })
99    }
100
101    /// Builds and validates the `Signal`.
102    ///
103    /// Consumes the builder and returns a fully constructed and validated [`Signal`].
104    ///
105    /// # Errors
106    ///
107    /// Returns an error if:
108    /// - Any required field is missing (name, start_bit, length, byte_order, unsigned, factor, offset, min, max)
109    /// - Name exceeds maximum length (32 characters)
110    /// - Signal length is invalid (zero or exceeds 64 bits)
111    /// - Min value exceeds max value
112    /// - Receivers fail to build
113    ///
114    /// # Examples
115    ///
116    /// ```rust,no_run
117    /// use dbc_rs::{SignalBuilder, ByteOrder, ReceiversBuilder};
118    ///
119    /// let signal = SignalBuilder::new()
120    ///     .name("EngineSpeed")
121    ///     .start_bit(0)
122    ///     .length(16)
123    ///     .byte_order(ByteOrder::LittleEndian)
124    ///     .unsigned(true)
125    ///     .factor(0.25)
126    ///     .offset(0.0)
127    ///     .min(0.0)
128    ///     .max(8000.0)
129    ///     .unit("rpm")
130    ///     .receivers(ReceiversBuilder::new().add_node("TCM"))
131    ///     .build()?;
132    ///
133    /// assert_eq!(signal.name(), "EngineSpeed");
134    /// assert_eq!(signal.factor(), 0.25);
135    /// # Ok::<(), dbc_rs::Error>(())
136    /// ```
137    pub fn build(self) -> Result<Signal> {
138        let (
139            name,
140            start_bit,
141            length,
142            byte_order,
143            unsigned,
144            factor,
145            offset,
146            min,
147            max,
148            unit,
149            receivers,
150            comment,
151        ) = self.extract_fields()?;
152        // Build receivers first (receivers is already ReceiversBuilder)
153        let built_receivers = receivers.build()?;
154        // Validate before construction
155        Signal::validate(&name, length, min, max)?;
156        // Use Cow::Owned for owned strings (no leak needed)
157        Ok(Signal::new(
158            name.into(),
159            start_bit,
160            length,
161            byte_order,
162            unsigned,
163            factor,
164            offset,
165            min,
166            max,
167            unit.map(|u| u.into()),
168            built_receivers,
169            comment.map(|c| c.into()),
170        ))
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    fn minimal_signal() -> SignalBuilder {
179        SignalBuilder::new()
180            .name("TestSignal")
181            .start_bit(0)
182            .length(8)
183            .byte_order(ByteOrder::LittleEndian)
184            .unsigned(true)
185            .factor(1.0)
186            .offset(0.0)
187            .min(0.0)
188            .max(255.0)
189            .receivers(ReceiversBuilder::new().none())
190    }
191
192    #[test]
193    fn test_signal_builder_new() {
194        let builder = SignalBuilder::new();
195        // Default builder should be empty
196        assert!(builder.build().is_err());
197    }
198
199    #[test]
200    fn test_signal_builder_default() {
201        let builder = SignalBuilder::default();
202        assert!(builder.build().is_err());
203    }
204
205    #[test]
206    fn test_signal_builder_missing_name() {
207        let result = SignalBuilder::new()
208            .start_bit(0)
209            .length(8)
210            .byte_order(ByteOrder::LittleEndian)
211            .unsigned(true)
212            .factor(1.0)
213            .offset(0.0)
214            .min(0.0)
215            .max(255.0)
216            .receivers(ReceiversBuilder::new().none())
217            .build();
218
219        assert!(result.is_err());
220    }
221
222    #[test]
223    fn test_signal_builder_missing_start_bit() {
224        let result = SignalBuilder::new()
225            .name("Test")
226            .length(8)
227            .byte_order(ByteOrder::LittleEndian)
228            .unsigned(true)
229            .factor(1.0)
230            .offset(0.0)
231            .min(0.0)
232            .max(255.0)
233            .receivers(ReceiversBuilder::new().none())
234            .build();
235
236        assert!(result.is_err());
237    }
238
239    #[test]
240    fn test_signal_builder_missing_length() {
241        let result = SignalBuilder::new()
242            .name("Test")
243            .start_bit(0)
244            .byte_order(ByteOrder::LittleEndian)
245            .unsigned(true)
246            .factor(1.0)
247            .offset(0.0)
248            .min(0.0)
249            .max(255.0)
250            .receivers(ReceiversBuilder::new().none())
251            .build();
252
253        assert!(result.is_err());
254    }
255
256    #[test]
257    fn test_signal_builder_missing_byte_order() {
258        let result = SignalBuilder::new()
259            .name("Test")
260            .start_bit(0)
261            .length(8)
262            .unsigned(true)
263            .factor(1.0)
264            .offset(0.0)
265            .min(0.0)
266            .max(255.0)
267            .receivers(ReceiversBuilder::new().none())
268            .build();
269
270        assert!(result.is_err());
271    }
272
273    #[test]
274    fn test_signal_builder_missing_unsigned() {
275        let result = SignalBuilder::new()
276            .name("Test")
277            .start_bit(0)
278            .length(8)
279            .byte_order(ByteOrder::LittleEndian)
280            .factor(1.0)
281            .offset(0.0)
282            .min(0.0)
283            .max(255.0)
284            .receivers(ReceiversBuilder::new().none())
285            .build();
286
287        assert!(result.is_err());
288    }
289
290    #[test]
291    fn test_signal_builder_missing_factor() {
292        let result = SignalBuilder::new()
293            .name("Test")
294            .start_bit(0)
295            .length(8)
296            .byte_order(ByteOrder::LittleEndian)
297            .unsigned(true)
298            .offset(0.0)
299            .min(0.0)
300            .max(255.0)
301            .receivers(ReceiversBuilder::new().none())
302            .build();
303
304        assert!(result.is_err());
305    }
306
307    #[test]
308    fn test_signal_builder_missing_offset() {
309        let result = SignalBuilder::new()
310            .name("Test")
311            .start_bit(0)
312            .length(8)
313            .byte_order(ByteOrder::LittleEndian)
314            .unsigned(true)
315            .factor(1.0)
316            .min(0.0)
317            .max(255.0)
318            .receivers(ReceiversBuilder::new().none())
319            .build();
320
321        assert!(result.is_err());
322    }
323
324    #[test]
325    fn test_signal_builder_missing_min() {
326        let result = SignalBuilder::new()
327            .name("Test")
328            .start_bit(0)
329            .length(8)
330            .byte_order(ByteOrder::LittleEndian)
331            .unsigned(true)
332            .factor(1.0)
333            .offset(0.0)
334            .max(255.0)
335            .receivers(ReceiversBuilder::new().none())
336            .build();
337
338        assert!(result.is_err());
339    }
340
341    #[test]
342    fn test_signal_builder_missing_max() {
343        let result = SignalBuilder::new()
344            .name("Test")
345            .start_bit(0)
346            .length(8)
347            .byte_order(ByteOrder::LittleEndian)
348            .unsigned(true)
349            .factor(1.0)
350            .offset(0.0)
351            .min(0.0)
352            .receivers(ReceiversBuilder::new().none())
353            .build();
354
355        assert!(result.is_err());
356    }
357
358    #[test]
359    fn test_signal_builder_validate_valid() {
360        let builder = minimal_signal();
361        let result = builder.validate();
362        assert!(result.is_ok());
363    }
364
365    #[test]
366    fn test_signal_builder_validate_start_bit_too_large() {
367        let builder = SignalBuilder::new()
368            .name("Test")
369            .start_bit(512) // > 511
370            .length(8)
371            .byte_order(ByteOrder::LittleEndian)
372            .unsigned(true)
373            .factor(1.0)
374            .offset(0.0)
375            .min(0.0)
376            .max(255.0)
377            .receivers(ReceiversBuilder::new().none());
378
379        let result = builder.validate();
380        assert!(result.is_err());
381    }
382
383    #[test]
384    fn test_signal_builder_validate_extends_beyond_512_bits() {
385        let builder = SignalBuilder::new()
386            .name("Test")
387            .start_bit(505)
388            .length(16) // 505 + 16 - 1 = 520 >= 512
389            .byte_order(ByteOrder::LittleEndian)
390            .unsigned(true)
391            .factor(1.0)
392            .offset(0.0)
393            .min(0.0)
394            .max(255.0)
395            .receivers(ReceiversBuilder::new().none());
396
397        let result = builder.validate();
398        assert!(result.is_err());
399    }
400
401    #[test]
402    fn test_signal_builder_validate_at_boundary() {
403        // 504 + 8 - 1 = 511 < 512, should pass
404        let builder = SignalBuilder::new()
405            .name("Test")
406            .start_bit(504)
407            .length(8)
408            .byte_order(ByteOrder::LittleEndian)
409            .unsigned(true)
410            .factor(1.0)
411            .offset(0.0)
412            .min(0.0)
413            .max(255.0)
414            .receivers(ReceiversBuilder::new().none());
415
416        let result = builder.validate();
417        assert!(result.is_ok());
418    }
419
420    #[test]
421    fn test_signal_builder_invalid_min_max_range() {
422        // min > max should fail
423        let result = SignalBuilder::new()
424            .name("Test")
425            .start_bit(0)
426            .length(8)
427            .byte_order(ByteOrder::LittleEndian)
428            .unsigned(true)
429            .factor(1.0)
430            .offset(0.0)
431            .min(100.0)
432            .max(0.0) // min > max
433            .receivers(ReceiversBuilder::new().none())
434            .build();
435
436        assert!(result.is_err());
437    }
438
439    #[test]
440    fn test_signal_builder_zero_length() {
441        let result = SignalBuilder::new()
442            .name("Test")
443            .start_bit(0)
444            .length(0) // Invalid
445            .byte_order(ByteOrder::LittleEndian)
446            .unsigned(true)
447            .factor(1.0)
448            .offset(0.0)
449            .min(0.0)
450            .max(255.0)
451            .receivers(ReceiversBuilder::new().none())
452            .build();
453
454        assert!(result.is_err());
455    }
456
457    #[test]
458    fn test_signal_builder_length_too_large() {
459        let result = SignalBuilder::new()
460            .name("Test")
461            .start_bit(0)
462            .length(513) // > 512
463            .byte_order(ByteOrder::LittleEndian)
464            .unsigned(true)
465            .factor(1.0)
466            .offset(0.0)
467            .min(0.0)
468            .max(255.0)
469            .receivers(ReceiversBuilder::new().none())
470            .build();
471
472        assert!(result.is_err());
473    }
474}