dbc_rs/signal/
signal_builder.rs

1#[cfg(any(feature = "alloc", feature = "kernel"))]
2use crate::compat::{Box, String, str_to_string};
3use crate::{
4    ByteOrder, Receivers, ReceiversBuilder,
5    error::{Error, Result, messages},
6    signal::Signal,
7};
8
9type SignalFields = (
10    String,
11    u16,
12    u16,
13    ByteOrder,
14    bool,
15    f64,
16    f64,
17    f64,
18    f64,
19    Option<String>,
20    Receivers<'static>,
21);
22
23#[derive(Debug, Clone)]
24pub struct SignalBuilder {
25    name: Option<String>,
26    start_bit: Option<u16>,
27    length: Option<u16>,
28    byte_order: ByteOrder,
29    unsigned: bool,
30    factor: f64,
31    offset: f64,
32    min: f64,
33    max: f64,
34    unit: Option<String>,
35    receivers: Receivers<'static>,
36}
37
38impl Default for SignalBuilder {
39    fn default() -> Self {
40        Self {
41            name: None,
42            start_bit: None,
43            length: None,
44            byte_order: ByteOrder::BigEndian,
45            unsigned: true,
46            factor: 1.0,
47            offset: 0.0,
48            min: 0.0,
49            max: 0.0,
50            unit: None,
51            receivers: ReceiversBuilder::new().broadcast().build().unwrap(),
52        }
53    }
54}
55
56impl SignalBuilder {
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    #[must_use]
62    pub fn name(mut self, name: impl AsRef<str>) -> Self {
63        self.name = Some(str_to_string(name));
64        self
65    }
66
67    #[must_use]
68    pub fn start_bit(mut self, start_bit: u16) -> Self {
69        self.start_bit = Some(start_bit);
70        self
71    }
72
73    #[must_use]
74    pub fn length(mut self, length: u16) -> Self {
75        self.length = Some(length);
76        self
77    }
78
79    #[must_use]
80    pub fn byte_order(mut self, byte_order: ByteOrder) -> Self {
81        self.byte_order = byte_order;
82        self
83    }
84
85    #[must_use]
86    pub fn unsigned(mut self, unsigned: bool) -> Self {
87        self.unsigned = unsigned;
88        self
89    }
90
91    #[must_use]
92    pub fn factor(mut self, factor: f64) -> Self {
93        self.factor = factor;
94        self
95    }
96
97    #[must_use]
98    pub fn offset(mut self, offset: f64) -> Self {
99        self.offset = offset;
100        self
101    }
102
103    #[must_use]
104    pub fn min(mut self, min: f64) -> Self {
105        self.min = min;
106        self
107    }
108
109    #[must_use]
110    pub fn max(mut self, max: f64) -> Self {
111        self.max = max;
112        self
113    }
114
115    #[must_use]
116    pub fn unit(mut self, unit: impl AsRef<str>) -> Self {
117        self.unit = Some(str_to_string(unit));
118        self
119    }
120
121    #[must_use]
122    pub fn no_unit(mut self) -> Self {
123        self.unit = None;
124        self
125    }
126
127    #[must_use]
128    pub fn receivers(mut self, receivers: Receivers<'static>) -> Self {
129        self.receivers = receivers;
130        self
131    }
132
133    fn extract_fields(self) -> Result<SignalFields> {
134        let name = self.name.ok_or_else(|| {
135            Error::Signal(crate::error::str_to_error_string(
136                messages::SIGNAL_NAME_EMPTY,
137            ))
138        })?;
139        let start_bit = self.start_bit.ok_or_else(|| {
140            Error::Signal(crate::error::str_to_error_string(
141                messages::SIGNAL_START_BIT_REQUIRED,
142            ))
143        })?;
144        let length = self.length.ok_or_else(|| {
145            Error::Signal(crate::error::str_to_error_string(
146                messages::SIGNAL_LENGTH_REQUIRED,
147            ))
148        })?;
149        Ok((
150            name,
151            start_bit,
152            length,
153            self.byte_order,
154            self.unsigned,
155            self.factor,
156            self.offset,
157            self.min,
158            self.max,
159            self.unit,
160            self.receivers,
161        ))
162    }
163
164    #[must_use = "validation result should be checked"]
165    pub fn validate(self) -> Result<Self> {
166        let (
167            name,
168            start_bit,
169            length,
170            byte_order,
171            unsigned,
172            factor,
173            offset,
174            min,
175            max,
176            unit,
177            receivers,
178        ) = self.extract_fields()?;
179
180        // Validate start_bit: must be between 0 and 511 (CAN FD maximum is 512 bits)
181        if start_bit > 511 {
182            return Err(Error::Signal(crate::error::str_to_error_string(
183                &crate::error::messages::signal_start_bit_invalid(&name, start_bit),
184            )));
185        }
186
187        // Validate that start_bit + length doesn't exceed CAN FD maximum (512 bits)
188        // Note: This is a basic sanity check. Full validation (including message DLC bounds
189        // and overlap detection) happens when the signal is added to a message.
190        let end_bit = start_bit + length - 1; // -1 because length includes the start bit
191        if end_bit >= 512 {
192            return Err(Error::Signal(crate::error::str_to_error_string(
193                &crate::error::messages::signal_extends_beyond_message(
194                    &name, start_bit, length, end_bit, 512, // max_bits (CAN FD maximum)
195                    64,  // dlc in bytes (64 bytes = 512 bits)
196                ),
197            )));
198        }
199
200        Signal::validate(&name, length, min, max).map_err(Error::from)?;
201        Ok(Self {
202            name: Some(name),
203            start_bit: Some(start_bit),
204            length: Some(length),
205            byte_order,
206            unsigned,
207            factor,
208            offset,
209            min,
210            max,
211            unit,
212            receivers,
213        })
214    }
215
216    pub fn build(self) -> Result<Signal<'static>> {
217        let (
218            name,
219            start_bit,
220            length,
221            byte_order,
222            unsigned,
223            factor,
224            offset,
225            min,
226            max,
227            unit,
228            receivers,
229        ) = self.extract_fields()?;
230        // Convert owned strings to static references by leaking Box<str>
231        let name_boxed: Box<str> = name.into_boxed_str();
232        let name_static: &'static str = Box::leak(name_boxed);
233        let unit_static: Option<&'static str> = if let Some(u) = unit {
234            let boxed: Box<str> = u.into_boxed_str();
235            Some(Box::leak(boxed))
236        } else {
237            None
238        };
239        // Validate before construction
240        Signal::validate(name_static, length, min, max).map_err(|e| match e {
241            crate::error::ParseError::Version(msg) => {
242                Error::Signal(crate::error::str_to_error_string(msg))
243            }
244            _ => Error::ParseError(e),
245        })?;
246        Ok(Signal::new(
247            name_static,
248            start_bit,
249            length,
250            byte_order,
251            unsigned,
252            factor,
253            offset,
254            min,
255            max,
256            unit_static,
257            receivers,
258        ))
259    }
260}