dbc_rs/extended_multiplexing/builder/
mod.rs

1use crate::{
2    Error, ExtendedMultiplexing, Result,
3    compat::{Vec as CompatVec, validate_name},
4};
5
6/// Builder for creating `ExtendedMultiplexing` programmatically.
7///
8/// This builder allows you to construct extended multiplexing entries when building DBC files
9/// programmatically. Extended multiplexing (SG_MUL_VAL_) entries define which multiplexer
10/// switch values activate specific multiplexed signals.
11///
12/// # Examples
13///
14/// ```rust,no_run
15/// use dbc_rs::ExtendedMultiplexingBuilder;
16///
17/// // Build an extended multiplexing entry
18/// let ext_mux = ExtendedMultiplexingBuilder::new()
19///     .message_id(500)
20///     .signal_name("Signal_A")
21///     .multiplexer_switch("Mux1")
22///     .add_value_range(0, 5)
23///     .add_value_range(10, 15)
24///     .build()?;
25///
26/// assert_eq!(ext_mux.message_id(), 500);
27/// assert_eq!(ext_mux.signal_name(), "Signal_A");
28/// assert_eq!(ext_mux.multiplexer_switch(), "Mux1");
29/// # Ok::<(), dbc_rs::Error>(())
30/// ```
31///
32/// # Feature Requirements
33///
34/// This builder requires the `std` feature to be enabled.
35#[derive(Debug, Clone)]
36pub struct ExtendedMultiplexingBuilder {
37    message_id: Option<u32>,
38    signal_name: Option<String>,
39    multiplexer_switch: Option<String>,
40    value_ranges: std::vec::Vec<(u64, u64)>,
41}
42
43impl ExtendedMultiplexingBuilder {
44    /// Creates a new `ExtendedMultiplexingBuilder` with no fields set.
45    ///
46    /// # Examples
47    ///
48    /// ```rust,no_run
49    /// use dbc_rs::ExtendedMultiplexingBuilder;
50    ///
51    /// let builder = ExtendedMultiplexingBuilder::new();
52    /// // Must set message_id, signal_name, multiplexer_switch, and at least one value range before building
53    /// # Ok::<(), dbc_rs::Error>(())
54    /// ```
55    pub fn new() -> Self {
56        Self {
57            message_id: None,
58            signal_name: None,
59            multiplexer_switch: None,
60            value_ranges: std::vec::Vec::new(),
61        }
62    }
63
64    /// Sets the message ID.
65    ///
66    /// # Arguments
67    ///
68    /// * `message_id` - The CAN message ID this extended multiplexing entry applies to
69    ///
70    /// # Examples
71    ///
72    /// ```rust,no_run
73    /// use dbc_rs::ExtendedMultiplexingBuilder;
74    ///
75    /// let builder = ExtendedMultiplexingBuilder::new()
76    ///     .message_id(500);
77    /// # Ok::<(), dbc_rs::Error>(())
78    /// ```
79    #[must_use = "builder method returns modified builder"]
80    pub fn message_id(mut self, message_id: u32) -> Self {
81        self.message_id = Some(message_id);
82        self
83    }
84
85    /// Sets the signal name.
86    ///
87    /// # Arguments
88    ///
89    /// * `signal_name` - The name of the multiplexed signal
90    ///
91    /// # Examples
92    ///
93    /// ```rust,no_run
94    /// use dbc_rs::ExtendedMultiplexingBuilder;
95    ///
96    /// let builder = ExtendedMultiplexingBuilder::new()
97    ///     .signal_name("Signal_A");
98    /// # Ok::<(), dbc_rs::Error>(())
99    /// ```
100    #[must_use = "builder method returns modified builder"]
101    pub fn signal_name(mut self, signal_name: impl AsRef<str>) -> Self {
102        self.signal_name = Some(signal_name.as_ref().to_string());
103        self
104    }
105
106    /// Sets the multiplexer switch name.
107    ///
108    /// # Arguments
109    ///
110    /// * `multiplexer_switch` - The name of the multiplexer switch signal
111    ///
112    /// # Examples
113    ///
114    /// ```rust,no_run
115    /// use dbc_rs::ExtendedMultiplexingBuilder;
116    ///
117    /// let builder = ExtendedMultiplexingBuilder::new()
118    ///     .multiplexer_switch("Mux1");
119    /// # Ok::<(), dbc_rs::Error>(())
120    /// ```
121    #[must_use = "builder method returns modified builder"]
122    pub fn multiplexer_switch(mut self, multiplexer_switch: impl AsRef<str>) -> Self {
123        self.multiplexer_switch = Some(multiplexer_switch.as_ref().to_string());
124        self
125    }
126
127    /// Adds a value range to the extended multiplexing entry.
128    ///
129    /// # Arguments
130    ///
131    /// * `min` - The minimum switch value (inclusive)
132    /// * `max` - The maximum switch value (inclusive)
133    ///
134    /// # Examples
135    ///
136    /// ```rust,no_run
137    /// use dbc_rs::ExtendedMultiplexingBuilder;
138    ///
139    /// let builder = ExtendedMultiplexingBuilder::new()
140    ///     .add_value_range(0, 5)
141    ///     .add_value_range(10, 15);
142    /// # Ok::<(), dbc_rs::Error>(())
143    /// ```
144    #[must_use = "builder method returns modified builder"]
145    pub fn add_value_range(mut self, min: u64, max: u64) -> Self {
146        self.value_ranges.push((min, max));
147        self
148    }
149
150    /// Builds the `ExtendedMultiplexing` from the builder configuration.
151    ///
152    /// This validates that all required fields have been set and constructs an
153    /// `ExtendedMultiplexing` instance.
154    ///
155    /// # Returns
156    ///
157    /// Returns `Ok(ExtendedMultiplexing)` if successful, or `Err(Error)` if:
158    /// - message_id is not set
159    /// - signal_name is not set or invalid
160    /// - multiplexer_switch is not set or invalid
161    /// - No value ranges have been added
162    /// - Any name exceeds MAX_NAME_SIZE
163    ///
164    /// # Examples
165    ///
166    /// ```rust,no_run
167    /// use dbc_rs::ExtendedMultiplexingBuilder;
168    ///
169    /// let ext_mux = ExtendedMultiplexingBuilder::new()
170    ///     .message_id(500)
171    ///     .signal_name("Signal_A")
172    ///     .multiplexer_switch("Mux1")
173    ///     .add_value_range(0, 5)
174    ///     .build()?;
175    /// # Ok::<(), dbc_rs::Error>(())
176    /// ```
177    ///
178    /// # Errors
179    ///
180    /// ```rust,no_run
181    /// use dbc_rs::ExtendedMultiplexingBuilder;
182    ///
183    /// // Missing message_id
184    /// let result = ExtendedMultiplexingBuilder::new()
185    ///     .signal_name("Signal_A")
186    ///     .multiplexer_switch("Mux1")
187    ///     .add_value_range(0, 5)
188    ///     .build();
189    /// assert!(result.is_err());
190    ///
191    /// // Missing value ranges
192    /// let result = ExtendedMultiplexingBuilder::new()
193    ///     .message_id(500)
194    ///     .signal_name("Signal_A")
195    ///     .multiplexer_switch("Mux1")
196    ///     .build();
197    /// assert!(result.is_err());
198    /// # Ok::<(), dbc_rs::Error>(())
199    /// ```
200    pub fn build(self) -> Result<ExtendedMultiplexing> {
201        let message_id = self.message_id.ok_or(Error::Expected("message_id is required"))?;
202
203        let signal_name_str = self.signal_name.ok_or(Error::Expected("signal_name is required"))?;
204        let signal_name = validate_name(&signal_name_str)
205            .map_err(|_| Error::Expected(Error::MAX_NAME_SIZE_EXCEEDED))?;
206
207        let multiplexer_switch_str = self
208            .multiplexer_switch
209            .ok_or(Error::Expected("multiplexer_switch is required"))?;
210        let multiplexer_switch = validate_name(&multiplexer_switch_str)
211            .map_err(|_| Error::Expected(Error::MAX_NAME_SIZE_EXCEEDED))?;
212
213        if self.value_ranges.is_empty() {
214            return Err(Error::Expected("at least one value range is required"));
215        }
216
217        // Convert std::vec::Vec to compat::Vec
218        let mut value_ranges: CompatVec<(u64, u64), 64> = CompatVec::new();
219        for (min, max) in self.value_ranges {
220            value_ranges
221                .push((min, max))
222                .map_err(|_| Error::Expected("too many value ranges (maximum 64)"))?;
223        }
224
225        Ok(ExtendedMultiplexing::new(
226            message_id,
227            signal_name,
228            multiplexer_switch,
229            value_ranges,
230        ))
231    }
232}
233
234impl Default for ExtendedMultiplexingBuilder {
235    fn default() -> Self {
236        Self::new()
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::ExtendedMultiplexingBuilder;
243
244    #[test]
245    fn test_extended_multiplexing_builder_basic() {
246        let ext_mux = ExtendedMultiplexingBuilder::new()
247            .message_id(500)
248            .signal_name("Signal_A")
249            .multiplexer_switch("Mux1")
250            .add_value_range(0, 5)
251            .build()
252            .unwrap();
253
254        assert_eq!(ext_mux.message_id(), 500);
255        assert_eq!(ext_mux.signal_name(), "Signal_A");
256        assert_eq!(ext_mux.multiplexer_switch(), "Mux1");
257        assert_eq!(ext_mux.value_ranges(), &[(0, 5)]);
258    }
259
260    #[test]
261    fn test_extended_multiplexing_builder_multiple_ranges() {
262        let ext_mux = ExtendedMultiplexingBuilder::new()
263            .message_id(500)
264            .signal_name("Signal_A")
265            .multiplexer_switch("Mux1")
266            .add_value_range(0, 5)
267            .add_value_range(10, 15)
268            .add_value_range(20, 25)
269            .build()
270            .unwrap();
271
272        assert_eq!(ext_mux.value_ranges().len(), 3);
273        assert_eq!(ext_mux.value_ranges()[0], (0, 5));
274        assert_eq!(ext_mux.value_ranges()[1], (10, 15));
275        assert_eq!(ext_mux.value_ranges()[2], (20, 25));
276    }
277
278    #[test]
279    fn test_extended_multiplexing_builder_missing_message_id() {
280        let result = ExtendedMultiplexingBuilder::new()
281            .signal_name("Signal_A")
282            .multiplexer_switch("Mux1")
283            .add_value_range(0, 5)
284            .build();
285        assert!(result.is_err());
286    }
287
288    #[test]
289    fn test_extended_multiplexing_builder_missing_signal_name() {
290        let result = ExtendedMultiplexingBuilder::new()
291            .message_id(500)
292            .multiplexer_switch("Mux1")
293            .add_value_range(0, 5)
294            .build();
295        assert!(result.is_err());
296    }
297
298    #[test]
299    fn test_extended_multiplexing_builder_missing_multiplexer_switch() {
300        let result = ExtendedMultiplexingBuilder::new()
301            .message_id(500)
302            .signal_name("Signal_A")
303            .add_value_range(0, 5)
304            .build();
305        assert!(result.is_err());
306    }
307
308    #[test]
309    fn test_extended_multiplexing_builder_no_value_ranges() {
310        let result = ExtendedMultiplexingBuilder::new()
311            .message_id(500)
312            .signal_name("Signal_A")
313            .multiplexer_switch("Mux1")
314            .build();
315        assert!(result.is_err());
316    }
317}