dbc_rs/message/
signals.rs

1use crate::{MAX_SIGNALS_PER_MESSAGE, Signal, compat::Vec};
2
3/// Encapsulates the signals array for a message
4///
5/// Uses `Vec<Signal>` for dynamic sizing.
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct Signals {
8    signals: Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }>,
9}
10
11impl From<&[Signal]> for Signals {
12    fn from(signals: &[Signal]) -> Self {
13        Self::from_slice(signals)
14    }
15}
16
17#[cfg(feature = "std")]
18impl From<std::vec::Vec<Signal>> for Signals {
19    fn from(signals: std::vec::Vec<Signal>) -> Self {
20        Self::from_slice(&signals)
21    }
22}
23
24impl From<Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }>> for Signals {
25    fn from(signals: Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }>) -> Self {
26        Self::from_slice(signals.as_slice())
27    }
28}
29
30impl Signals {
31    /// Create Signals from a slice of signals by cloning them
32    pub(crate) fn from_slice(signals: &[Signal]) -> Self {
33        let count = signals.len().min(MAX_SIGNALS_PER_MESSAGE);
34        let signals_vec: Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }> =
35            signals.iter().take(count).cloned().collect();
36        Self {
37            signals: signals_vec,
38        }
39    }
40
41    /// Get an iterator over the signals
42    ///
43    /// # Examples
44    ///
45    /// ```rust,no_run
46    /// use dbc_rs::Dbc;
47    ///
48    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM\n SG_ RPM : 0|16@1+ (0.25,0) [0|8000] \"rpm\"")?;
49    /// let message = dbc.messages().at(0).unwrap();
50    /// for signal in message.signals().iter() {
51    ///     println!("Signal: {} (start: {}, length: {})", signal.name(), signal.start_bit(), signal.length());
52    /// }
53    /// # Ok::<(), dbc_rs::Error>(())
54    /// ```
55    #[inline]
56    #[must_use = "iterator is lazy and does nothing unless consumed"]
57    pub fn iter(&self) -> impl Iterator<Item = &Signal> + '_ {
58        self.signals.iter()
59    }
60
61    /// Get the number of signals
62    ///
63    /// # Examples
64    ///
65    /// ```rust,no_run
66    /// use dbc_rs::Dbc;
67    ///
68    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM\n SG_ RPM : 0|16@1+ (0.25,0) [0|8000] \"rpm\"")?;
69    /// let message = dbc.messages().at(0).unwrap();
70    /// assert_eq!(message.signals().len(), 1);
71    /// # Ok::<(), dbc_rs::Error>(())
72    /// ```
73    #[inline]
74    #[must_use = "return value should be used"]
75    pub fn len(&self) -> usize {
76        self.signals.len()
77    }
78
79    /// Returns `true` if there are no signals
80    ///
81    /// # Examples
82    ///
83    /// ```rust,no_run
84    /// use dbc_rs::Dbc;
85    ///
86    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
87    /// let message = dbc.messages().at(0).unwrap();
88    /// assert!(message.signals().is_empty());
89    /// # Ok::<(), dbc_rs::Error>(())
90    /// ```
91    #[inline]
92    #[must_use = "return value should be used"]
93    pub fn is_empty(&self) -> bool {
94        self.len() == 0
95    }
96
97    /// Get a signal by index, or None if index is out of bounds
98    ///
99    /// # Examples
100    ///
101    /// ```rust,no_run
102    /// use dbc_rs::Dbc;
103    ///
104    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM\n SG_ RPM : 0|16@1+ (0.25,0) [0|8000] \"rpm\"")?;
105    /// let message = dbc.messages().at(0).unwrap();
106    /// if let Some(signal) = message.signals().at(0) {
107    ///     assert_eq!(signal.name(), "RPM");
108    /// }
109    /// # Ok::<(), dbc_rs::Error>(())
110    /// ```
111    #[inline]
112    #[must_use = "return value should be used"]
113    pub fn at(&self, index: usize) -> Option<&Signal> {
114        self.signals.get(index)
115    }
116
117    /// Find a signal by name, or None if not found
118    ///
119    /// # Examples
120    ///
121    /// ```rust,no_run
122    /// use dbc_rs::Dbc;
123    ///
124    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM\n SG_ RPM : 0|16@1+ (0.25,0) [0|8000] \"rpm\"")?;
125    /// let message = dbc.messages().at(0).unwrap();
126    /// if let Some(signal) = message.signals().find("RPM") {
127    ///     assert_eq!(signal.name(), "RPM");
128    ///     assert_eq!(signal.factor(), 0.25);
129    /// }
130    /// # Ok::<(), dbc_rs::Error>(())
131    /// ```
132    #[must_use = "return value should be used"]
133    pub fn find(&self, name: &str) -> Option<&Signal> {
134        self.iter().find(|s| s.name() == name)
135    }
136
137    /// Find a mutable reference to a signal by name.
138    /// Used internally during parsing when CM_ entries are processed after signals.
139    #[must_use = "return value should be used"]
140    pub(crate) fn find_mut(&mut self, name: &str) -> Option<&mut Signal> {
141        self.signals.iter_mut().find(|s| s.name() == name)
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::Signals;
148    use crate::{Parser, Signal};
149
150    // Tests for from_slice
151    #[cfg(feature = "std")]
152    mod test_from_slice {
153        use super::*;
154
155        #[test]
156        fn test_signals_from_slice() {
157            let signal1 = Signal::parse(
158                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
159            )
160            .unwrap();
161            let signal2 = Signal::parse(
162                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
163            )
164            .unwrap();
165
166            let signals = Signals::from_slice(&[signal1, signal2]);
167            assert_eq!(signals.len(), 2);
168            assert!(!signals.is_empty());
169            assert_eq!(signals.at(0).unwrap().name(), "Signal1");
170            assert_eq!(signals.at(1).unwrap().name(), "Signal2");
171        }
172
173        #[test]
174        fn test_signals_from_signals_slice_empty() {
175            let signals = Signals::from_slice(&[]);
176            assert_eq!(signals.len(), 0);
177            assert!(signals.is_empty());
178            assert!(signals.at(0).is_none());
179        }
180
181        #[test]
182        fn test_signals_from_slice_multiple() {
183            // Test with multiple signals to verify capacity handling
184            let signal1 = Signal::parse(
185                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
186            )
187            .unwrap();
188            let signal2 = Signal::parse(
189                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
190            )
191            .unwrap();
192            let signal3 = Signal::parse(
193                &mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap(),
194            )
195            .unwrap();
196
197            let signals = Signals::from_slice(&[signal1, signal2, signal3]);
198            assert_eq!(signals.len(), 3);
199            assert_eq!(signals.at(0).unwrap().name(), "Signal1");
200            assert_eq!(signals.at(1).unwrap().name(), "Signal2");
201            assert_eq!(signals.at(2).unwrap().name(), "Signal3");
202        }
203    }
204
205    // Tests for iter()
206    mod test_iter {
207        use super::*;
208
209        #[test]
210        fn test_signals_iter() {
211            let signal1 = Signal::parse(
212                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
213            )
214            .unwrap();
215            let signal2 = Signal::parse(
216                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
217            )
218            .unwrap();
219
220            let signals = Signals::from_slice(&[signal1, signal2]);
221            let mut iter = signals.iter();
222            assert_eq!(iter.next().unwrap().name(), "RPM");
223            assert_eq!(iter.next().unwrap().name(), "Temp");
224            assert!(iter.next().is_none());
225        }
226
227        #[test]
228        fn test_signals_iter_empty() {
229            let signals = Signals::from_slice(&[]);
230            let mut iter = signals.iter();
231            assert!(iter.next().is_none());
232        }
233
234        #[test]
235        fn test_signals_iter_multiple() {
236            let signal1 = Signal::parse(
237                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
238            )
239            .unwrap();
240            let signal2 = Signal::parse(
241                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
242            )
243            .unwrap();
244            let signal3 = Signal::parse(
245                &mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap(),
246            )
247            .unwrap();
248
249            let signals = Signals::from_slice(&[signal1, signal2, signal3]);
250            let mut iter = signals.iter();
251            assert_eq!(iter.next().unwrap().name(), "Signal1");
252            assert_eq!(iter.next().unwrap().name(), "Signal2");
253            assert_eq!(iter.next().unwrap().name(), "Signal3");
254            assert!(iter.next().is_none());
255        }
256    }
257
258    // Tests for len()
259    mod test_len {
260        use super::*;
261
262        #[test]
263        fn test_signals_len() {
264            let signals = Signals::from_slice(&[]);
265            assert_eq!(signals.len(), 0);
266
267            let signal1 = Signal::parse(
268                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
269            )
270            .unwrap();
271            let signals = Signals::from_slice(&[signal1]);
272            assert_eq!(signals.len(), 1);
273        }
274    }
275
276    // Tests for is_empty()
277    mod test_is_empty {
278        use super::*;
279
280        #[test]
281        fn test_signals_is_empty() {
282            let signals = Signals::from_slice(&[]);
283            assert!(signals.is_empty());
284            assert_eq!(signals.len(), 0);
285
286            let signal1 = Signal::parse(
287                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
288            )
289            .unwrap();
290            let signals = Signals::from_slice(&[signal1]);
291            assert!(!signals.is_empty());
292        }
293    }
294
295    // Tests for at()
296    mod test_at {
297        use super::*;
298
299        #[test]
300        fn test_signals_at() {
301            let signal1 = Signal::parse(
302                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
303            )
304            .unwrap();
305            let signal2 = Signal::parse(
306                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
307            )
308            .unwrap();
309
310            let signals = Signals::from_slice(&[signal1, signal2]);
311            assert_eq!(signals.at(0).unwrap().name(), "RPM");
312            assert_eq!(signals.at(1).unwrap().name(), "Temp");
313            assert!(signals.at(2).is_none());
314        }
315
316        #[test]
317        fn test_signals_at_out_of_bounds() {
318            let signal = Signal::parse(
319                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
320            )
321            .unwrap();
322
323            let signals = Signals::from_slice(&[signal]);
324
325            // Valid index
326            assert!(signals.at(0).is_some());
327            assert_eq!(signals.at(0).unwrap().name(), "RPM");
328
329            // Out of bounds
330            assert!(signals.at(1).is_none());
331            assert!(signals.at(100).is_none());
332            assert!(signals.at(usize::MAX).is_none());
333        }
334    }
335
336    // Tests for find()
337    mod test_find {
338        use super::*;
339
340        #[test]
341        fn test_signals_find() {
342            let signal1 = Signal::parse(
343                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
344            )
345            .unwrap();
346            let signal2 = Signal::parse(
347                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
348            )
349            .unwrap();
350
351            let signals = Signals::from_slice(&[signal1, signal2]);
352            assert_eq!(signals.find("RPM").unwrap().name(), "RPM");
353            assert_eq!(signals.find("Temp").unwrap().name(), "Temp");
354            assert!(signals.find("Nonexistent").is_none());
355        }
356
357        #[test]
358        fn test_signals_find_case_sensitive() {
359            let signal1 = Signal::parse(
360                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
361            )
362            .unwrap();
363            let signal2 = Signal::parse(
364                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
365            )
366            .unwrap();
367
368            let signals = Signals::from_slice(&[signal1, signal2]);
369
370            // Exact match
371            assert!(signals.find("RPM").is_some());
372            assert_eq!(signals.find("RPM").unwrap().name(), "RPM");
373
374            // Case sensitive - should not find
375            assert!(signals.find("rpm").is_none());
376            assert!(signals.find("Rpm").is_none());
377
378            // Find second signal
379            assert!(signals.find("Temp").is_some());
380            assert_eq!(signals.find("Temp").unwrap().name(), "Temp");
381
382            // Not found
383            assert!(signals.find("Nonexistent").is_none());
384            assert!(signals.find("").is_none());
385        }
386
387        #[test]
388        fn test_signals_find_empty_collection() {
389            let signals = Signals::from_slice(&[]);
390            assert!(signals.find("RPM").is_none());
391            assert!(signals.find("").is_none());
392        }
393
394        #[test]
395        fn test_signals_find_not_found() {
396            let signal1 = Signal::parse(
397                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
398            )
399            .unwrap();
400
401            let signals = Signals::from_slice(&[signal1]);
402            assert!(signals.find("Nonexistent").is_none());
403            assert!(signals.find("").is_none());
404            assert!(signals.find("signal1").is_none()); // Case sensitive
405        }
406
407        #[test]
408        fn test_signals_find_first_match() {
409            let signal1 = Signal::parse(
410                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
411            )
412            .unwrap();
413            let signal2 = Signal::parse(
414                &mut Parser::new(b"SG_ Signal1 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
415            )
416            .unwrap(); // Same name (shouldn't happen in practice but test the behavior)
417
418            let signals = Signals::from_slice(&[signal1, signal2]);
419            // Should find the first match
420            let found = signals.find("Signal1");
421            assert!(found.is_some());
422            assert_eq!(found.unwrap().start_bit(), 0); // First signal
423        }
424    }
425}