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},
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(Error::signal(crate::error::lang::SIGNAL_NAME_EMPTY))?;
135        let start_bit = self
136            .start_bit
137            .ok_or(Error::signal(crate::error::lang::SIGNAL_START_BIT_REQUIRED))?;
138        let length =
139            self.length.ok_or(Error::signal(crate::error::lang::SIGNAL_LENGTH_REQUIRED))?;
140        Ok((
141            name,
142            start_bit,
143            length,
144            self.byte_order,
145            self.unsigned,
146            self.factor,
147            self.offset,
148            self.min,
149            self.max,
150            self.unit,
151            self.receivers,
152        ))
153    }
154
155    #[must_use = "validation result should be checked"]
156    pub fn validate(self) -> Result<Self> {
157        let (
158            name,
159            start_bit,
160            length,
161            byte_order,
162            unsigned,
163            factor,
164            offset,
165            min,
166            max,
167            unit,
168            receivers,
169        ) = self.extract_fields()?;
170
171        // Validate start_bit: must be between 0 and 511 (CAN FD maximum is 512 bits)
172        if start_bit > 511 {
173            return Err(Error::signal(
174                crate::error::lang::SIGNAL_PARSE_INVALID_START_BIT,
175            ));
176        }
177
178        // Validate that start_bit + length doesn't exceed CAN FD maximum (512 bits)
179        // Note: This is a basic sanity check. Full validation (including message DLC bounds
180        // and overlap detection) happens when the signal is added to a message.
181        let end_bit = start_bit + length - 1; // -1 because length includes the start bit
182        if end_bit >= 512 {
183            return Err(Error::signal(
184                crate::error::lang::SIGNAL_EXTENDS_BEYOND_MESSAGE,
185            ));
186        }
187
188        Signal::validate(&name, length, min, max).map_err(Error::from)?;
189        Ok(Self {
190            name: Some(name),
191            start_bit: Some(start_bit),
192            length: Some(length),
193            byte_order,
194            unsigned,
195            factor,
196            offset,
197            min,
198            max,
199            unit,
200            receivers,
201        })
202    }
203
204    pub fn build(self) -> Result<Signal<'static>> {
205        let (
206            name,
207            start_bit,
208            length,
209            byte_order,
210            unsigned,
211            factor,
212            offset,
213            min,
214            max,
215            unit,
216            receivers,
217        ) = self.extract_fields()?;
218        // Convert owned strings to static references by leaking Box<str>
219        let name_boxed: Box<str> = name.into_boxed_str();
220        let name_static: &'static str = Box::leak(name_boxed);
221        let unit_static: Option<&'static str> = if let Some(u) = unit {
222            let boxed: Box<str> = u.into_boxed_str();
223            Some(Box::leak(boxed))
224        } else {
225            None
226        };
227        // Validate before construction
228        Signal::validate(name_static, length, min, max).map_err(|e| match e {
229            crate::error::ParseError::Signal(msg) => Error::signal(msg),
230            _ => Error::ParseError(e),
231        })?;
232        Ok(Signal::new(
233            name_static,
234            start_bit,
235            length,
236            byte_order,
237            unsigned,
238            factor,
239            offset,
240            min,
241            max,
242            unit_static,
243            receivers,
244        ))
245    }
246}