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
138#[cfg(test)]
139mod tests {
140    use super::Signals;
141    use crate::{Parser, Signal};
142
143    // Tests for from_slice
144    #[cfg(feature = "std")]
145    mod test_from_slice {
146        use super::*;
147
148        #[test]
149        fn test_signals_from_slice() {
150            let signal1 = Signal::parse(
151                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
152            )
153            .unwrap();
154            let signal2 = Signal::parse(
155                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
156            )
157            .unwrap();
158
159            let signals = Signals::from_slice(&[signal1, signal2]);
160            assert_eq!(signals.len(), 2);
161            assert!(!signals.is_empty());
162            assert_eq!(signals.at(0).unwrap().name(), "Signal1");
163            assert_eq!(signals.at(1).unwrap().name(), "Signal2");
164        }
165
166        #[test]
167        fn test_signals_from_signals_slice_empty() {
168            let signals = Signals::from_slice(&[]);
169            assert_eq!(signals.len(), 0);
170            assert!(signals.is_empty());
171            assert!(signals.at(0).is_none());
172        }
173
174        #[test]
175        fn test_signals_from_slice_multiple() {
176            // Test with multiple signals to verify capacity handling
177            let signal1 = Signal::parse(
178                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
179            )
180            .unwrap();
181            let signal2 = Signal::parse(
182                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
183            )
184            .unwrap();
185            let signal3 = Signal::parse(
186                &mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap(),
187            )
188            .unwrap();
189
190            let signals = Signals::from_slice(&[signal1, signal2, signal3]);
191            assert_eq!(signals.len(), 3);
192            assert_eq!(signals.at(0).unwrap().name(), "Signal1");
193            assert_eq!(signals.at(1).unwrap().name(), "Signal2");
194            assert_eq!(signals.at(2).unwrap().name(), "Signal3");
195        }
196    }
197
198    // Tests for iter()
199    mod test_iter {
200        use super::*;
201
202        #[test]
203        fn test_signals_iter() {
204            let signal1 = Signal::parse(
205                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
206            )
207            .unwrap();
208            let signal2 = Signal::parse(
209                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
210            )
211            .unwrap();
212
213            let signals = Signals::from_slice(&[signal1, signal2]);
214            let mut iter = signals.iter();
215            assert_eq!(iter.next().unwrap().name(), "RPM");
216            assert_eq!(iter.next().unwrap().name(), "Temp");
217            assert!(iter.next().is_none());
218        }
219
220        #[test]
221        fn test_signals_iter_empty() {
222            let signals = Signals::from_slice(&[]);
223            let mut iter = signals.iter();
224            assert!(iter.next().is_none());
225        }
226
227        #[test]
228        fn test_signals_iter_multiple() {
229            let signal1 = Signal::parse(
230                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
231            )
232            .unwrap();
233            let signal2 = Signal::parse(
234                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
235            )
236            .unwrap();
237            let signal3 = Signal::parse(
238                &mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap(),
239            )
240            .unwrap();
241
242            let signals = Signals::from_slice(&[signal1, signal2, signal3]);
243            let mut iter = signals.iter();
244            assert_eq!(iter.next().unwrap().name(), "Signal1");
245            assert_eq!(iter.next().unwrap().name(), "Signal2");
246            assert_eq!(iter.next().unwrap().name(), "Signal3");
247            assert!(iter.next().is_none());
248        }
249    }
250
251    // Tests for len()
252    mod test_len {
253        use super::*;
254
255        #[test]
256        fn test_signals_len() {
257            let signals = Signals::from_slice(&[]);
258            assert_eq!(signals.len(), 0);
259
260            let signal1 = Signal::parse(
261                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
262            )
263            .unwrap();
264            let signals = Signals::from_slice(&[signal1]);
265            assert_eq!(signals.len(), 1);
266        }
267    }
268
269    // Tests for is_empty()
270    mod test_is_empty {
271        use super::*;
272
273        #[test]
274        fn test_signals_is_empty() {
275            let signals = Signals::from_slice(&[]);
276            assert!(signals.is_empty());
277            assert_eq!(signals.len(), 0);
278
279            let signal1 = Signal::parse(
280                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
281            )
282            .unwrap();
283            let signals = Signals::from_slice(&[signal1]);
284            assert!(!signals.is_empty());
285        }
286    }
287
288    // Tests for at()
289    mod test_at {
290        use super::*;
291
292        #[test]
293        fn test_signals_at() {
294            let signal1 = Signal::parse(
295                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
296            )
297            .unwrap();
298            let signal2 = Signal::parse(
299                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
300            )
301            .unwrap();
302
303            let signals = Signals::from_slice(&[signal1, signal2]);
304            assert_eq!(signals.at(0).unwrap().name(), "RPM");
305            assert_eq!(signals.at(1).unwrap().name(), "Temp");
306            assert!(signals.at(2).is_none());
307        }
308
309        #[test]
310        fn test_signals_at_out_of_bounds() {
311            let signal = Signal::parse(
312                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
313            )
314            .unwrap();
315
316            let signals = Signals::from_slice(&[signal]);
317
318            // Valid index
319            assert!(signals.at(0).is_some());
320            assert_eq!(signals.at(0).unwrap().name(), "RPM");
321
322            // Out of bounds
323            assert!(signals.at(1).is_none());
324            assert!(signals.at(100).is_none());
325            assert!(signals.at(usize::MAX).is_none());
326        }
327    }
328
329    // Tests for find()
330    mod test_find {
331        use super::*;
332
333        #[test]
334        fn test_signals_find() {
335            let signal1 = Signal::parse(
336                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
337            )
338            .unwrap();
339            let signal2 = Signal::parse(
340                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
341            )
342            .unwrap();
343
344            let signals = Signals::from_slice(&[signal1, signal2]);
345            assert_eq!(signals.find("RPM").unwrap().name(), "RPM");
346            assert_eq!(signals.find("Temp").unwrap().name(), "Temp");
347            assert!(signals.find("Nonexistent").is_none());
348        }
349
350        #[test]
351        fn test_signals_find_case_sensitive() {
352            let signal1 = Signal::parse(
353                &mut Parser::new(b"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\"").unwrap(),
354            )
355            .unwrap();
356            let signal2 = Signal::parse(
357                &mut Parser::new(b"SG_ Temp : 16|8@0- (1,-40) [-40|215] \"\xC2\xB0C\"").unwrap(),
358            )
359            .unwrap();
360
361            let signals = Signals::from_slice(&[signal1, signal2]);
362
363            // Exact match
364            assert!(signals.find("RPM").is_some());
365            assert_eq!(signals.find("RPM").unwrap().name(), "RPM");
366
367            // Case sensitive - should not find
368            assert!(signals.find("rpm").is_none());
369            assert!(signals.find("Rpm").is_none());
370
371            // Find second signal
372            assert!(signals.find("Temp").is_some());
373            assert_eq!(signals.find("Temp").unwrap().name(), "Temp");
374
375            // Not found
376            assert!(signals.find("Nonexistent").is_none());
377            assert!(signals.find("").is_none());
378        }
379
380        #[test]
381        fn test_signals_find_empty_collection() {
382            let signals = Signals::from_slice(&[]);
383            assert!(signals.find("RPM").is_none());
384            assert!(signals.find("").is_none());
385        }
386
387        #[test]
388        fn test_signals_find_not_found() {
389            let signal1 = Signal::parse(
390                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
391            )
392            .unwrap();
393
394            let signals = Signals::from_slice(&[signal1]);
395            assert!(signals.find("Nonexistent").is_none());
396            assert!(signals.find("").is_none());
397            assert!(signals.find("signal1").is_none()); // Case sensitive
398        }
399
400        #[test]
401        fn test_signals_find_first_match() {
402            let signal1 = Signal::parse(
403                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
404            )
405            .unwrap();
406            let signal2 = Signal::parse(
407                &mut Parser::new(b"SG_ Signal1 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
408            )
409            .unwrap(); // Same name (shouldn't happen in practice but test the behavior)
410
411            let signals = Signals::from_slice(&[signal1, signal2]);
412            // Should find the first match
413            let found = signals.find("Signal1");
414            assert!(found.is_some());
415            assert_eq!(found.unwrap().start_bit(), 0); // First signal
416        }
417    }
418}