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}