dbc_rs/message/
signals.rs

1use crate::Signal;
2
3/// Iterator over signals in a Signals collection
4struct SignalsIter<'a, 'b> {
5    signals: &'b [Option<Signal<'a>>],
6    count: usize,
7    pos: usize,
8}
9
10impl<'a, 'b> Iterator for SignalsIter<'a, 'b> {
11    type Item = &'b Signal<'a>;
12
13    #[inline]
14    fn next(&mut self) -> Option<Self::Item> {
15        while self.pos < self.count {
16            let result = self.signals[self.pos].as_ref();
17            self.pos += 1;
18            if let Some(sig) = result {
19                return Some(sig);
20            }
21        }
22        None
23    }
24}
25
26// Maximum signals per message
27//
28// Defaults to 64, but can be overridden at build time by setting the `DBC_MAX_SIGNALS_PER_MESSAGE`
29// environment variable:
30// ```bash
31// DBC_MAX_SIGNALS_PER_MESSAGE=128 cargo build
32// ```
33//
34// The value must be a valid positive integer. If not set or invalid, defaults to 64.
35// This constant is generated by build.rs at compile time.
36include!(concat!(env!("OUT_DIR"), "/limits.rs"));
37
38/// Encapsulates the signals array and count for a message
39///
40/// Storage strategy:
41/// - `no_std`: Uses fixed-size array `[Option<Signal>; MAX_SIGNALS_PER_MESSAGE]`
42/// - `alloc` or `kernel`: Uses heap-allocated `Box<[Option<Signal>]>` for dynamic sizing
43#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub struct Signals<'a> {
45    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
46    signals: [Option<Signal<'a>>; MAX_SIGNALS_PER_MESSAGE],
47    #[cfg(any(feature = "alloc", feature = "kernel"))]
48    signals: alloc::boxed::Box<[Option<Signal<'a>>]>,
49    signal_count: usize,
50}
51
52impl<'a> Signals<'a> {
53    /// Create Signals from a slice of signals by cloning them
54    #[allow(dead_code)] // Only used by builders (std-only)
55    pub(crate) fn from_signals_slice(signals: &[Signal<'a>]) -> Self {
56        let count = signals.len().min(MAX_SIGNALS_PER_MESSAGE);
57
58        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
59        {
60            let mut signals_array: [Option<Signal<'a>>; MAX_SIGNALS_PER_MESSAGE] =
61                [const { None }; MAX_SIGNALS_PER_MESSAGE];
62            for (i, signal) in signals.iter().take(MAX_SIGNALS_PER_MESSAGE).enumerate() {
63                signals_array[i] = Some(signal.clone());
64            }
65            Self {
66                signals: signals_array,
67                signal_count: count,
68            }
69        }
70
71        #[cfg(any(feature = "alloc", feature = "kernel"))]
72        {
73            use crate::compat::vec_with_capacity;
74            use alloc::vec::Vec;
75            let mut signals_vec: Vec<Option<Signal<'a>>> = vec_with_capacity(count);
76            for signal in signals.iter().take(count) {
77                signals_vec.push(Some(signal.clone()));
78            }
79            Self {
80                signals: signals_vec.into_boxed_slice(),
81                signal_count: count,
82            }
83        }
84    }
85
86    /// Create Signals from a slice of `Option<Signal>` and count
87    pub(crate) fn from_options_slice(signals: &[Option<Signal<'a>>], signal_count: usize) -> Self {
88        let count = signal_count.min(MAX_SIGNALS_PER_MESSAGE).min(signals.len());
89
90        #[cfg(not(any(feature = "alloc", feature = "kernel")))]
91        {
92            let mut signals_array: [Option<Signal<'a>>; MAX_SIGNALS_PER_MESSAGE] =
93                [const { None }; MAX_SIGNALS_PER_MESSAGE];
94            for (i, signal_opt) in signals.iter().take(count).enumerate() {
95                signals_array[i] = signal_opt.clone();
96            }
97            Self {
98                signals: signals_array,
99                signal_count: count,
100            }
101        }
102
103        #[cfg(any(feature = "alloc", feature = "kernel"))]
104        {
105            use crate::compat::vec_with_capacity;
106            use alloc::vec::Vec;
107            let mut signals_vec: Vec<Option<Signal<'a>>> = vec_with_capacity(count);
108            for signal_opt in signals.iter().take(count) {
109                signals_vec.push(signal_opt.clone());
110            }
111            Self {
112                signals: signals_vec.into_boxed_slice(),
113                signal_count: count,
114            }
115        }
116    }
117
118    /// Get an iterator over the signals
119    ///
120    /// # Examples
121    ///
122    /// ```rust,no_run
123    /// use dbc_rs::Dbc;
124    ///
125    /// 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\"")?;
126    /// let message = dbc.messages().at(0).unwrap();
127    /// for signal in message.signals().iter() {
128    ///     println!("Signal: {} (start: {}, length: {})", signal.name(), signal.start_bit(), signal.length());
129    /// }
130    /// # Ok::<(), dbc_rs::Error>(())
131    /// ```
132    #[inline]
133    #[must_use = "iterator is lazy and does nothing unless consumed"]
134    pub fn iter(&self) -> impl Iterator<Item = &Signal<'a>> + '_ {
135        let signals_slice: &[Option<Signal<'a>>] = &self.signals;
136        SignalsIter {
137            signals: signals_slice,
138            count: self.signal_count,
139            pos: 0,
140        }
141    }
142
143    /// Get the number of signals
144    ///
145    /// # Examples
146    ///
147    /// ```rust,no_run
148    /// use dbc_rs::Dbc;
149    ///
150    /// 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\"")?;
151    /// let message = dbc.messages().at(0).unwrap();
152    /// assert_eq!(message.signals().len(), 1);
153    /// # Ok::<(), dbc_rs::Error>(())
154    /// ```
155    #[inline]
156    #[must_use]
157    pub fn len(&self) -> usize {
158        self.signal_count
159    }
160
161    /// Returns `true` if there are no signals
162    ///
163    /// # Examples
164    ///
165    /// ```rust,no_run
166    /// use dbc_rs::Dbc;
167    ///
168    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
169    /// let message = dbc.messages().at(0).unwrap();
170    /// assert!(message.signals().is_empty());
171    /// # Ok::<(), dbc_rs::Error>(())
172    /// ```
173    #[inline]
174    #[must_use]
175    pub fn is_empty(&self) -> bool {
176        self.len() == 0
177    }
178
179    /// Get a signal by index, or None if index is out of bounds
180    ///
181    /// # Examples
182    ///
183    /// ```rust,no_run
184    /// use dbc_rs::Dbc;
185    ///
186    /// 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\"")?;
187    /// let message = dbc.messages().at(0).unwrap();
188    /// if let Some(signal) = message.signals().at(0) {
189    ///     assert_eq!(signal.name(), "RPM");
190    /// }
191    /// # Ok::<(), dbc_rs::Error>(())
192    /// ```
193    #[inline]
194    #[must_use]
195    pub fn at(&self, index: usize) -> Option<&Signal<'a>> {
196        if index >= self.signal_count {
197            return None;
198        }
199        self.signals[index].as_ref()
200    }
201
202    /// Find a signal by name, or None if not found
203    ///
204    /// # Examples
205    ///
206    /// ```rust,no_run
207    /// use dbc_rs::Dbc;
208    ///
209    /// 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\"")?;
210    /// let message = dbc.messages().at(0).unwrap();
211    /// if let Some(signal) = message.signals().find("RPM") {
212    ///     assert_eq!(signal.name(), "RPM");
213    ///     assert_eq!(signal.factor(), 0.25);
214    /// }
215    /// # Ok::<(), dbc_rs::Error>(())
216    /// ```
217    #[must_use]
218    pub fn find(&self, name: &str) -> Option<&Signal<'a>> {
219        self.iter().find(|s| s.name() == name)
220    }
221
222    /// Get the maximum capacity (for limit checking during parsing)
223    pub(crate) const fn max_capacity() -> usize {
224        MAX_SIGNALS_PER_MESSAGE
225    }
226
227    /// Create a temporary buffer for parsing (no alloc in no_std)
228    /// Returns a buffer that can hold up to MAX_SIGNALS_PER_MESSAGE signals
229    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
230    pub(crate) fn new_parse_buffer<'b>() -> [Option<Signal<'b>>; MAX_SIGNALS_PER_MESSAGE] {
231        [const { None }; MAX_SIGNALS_PER_MESSAGE]
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::Signals;
238    use crate::{Parser, Signal};
239
240    // Tests that require alloc or kernel feature (for from_signals_slice)
241    #[cfg(any(feature = "alloc", feature = "kernel"))]
242    mod tests_with_alloc {
243        use super::*;
244
245        #[test]
246        fn test_signals_from_signals_slice() {
247            let signal1 = Signal::parse(
248                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
249            )
250            .unwrap();
251            let signal2 = Signal::parse(
252                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
253            )
254            .unwrap();
255
256            let signals = Signals::from_signals_slice(&[signal1, signal2]);
257            assert_eq!(signals.len(), 2);
258            assert!(!signals.is_empty());
259            assert_eq!(signals.at(0).unwrap().name(), "Signal1");
260            assert_eq!(signals.at(1).unwrap().name(), "Signal2");
261        }
262
263        #[test]
264        fn test_signals_from_signals_slice_empty() {
265            let signals = Signals::from_signals_slice(&[]);
266            assert_eq!(signals.len(), 0);
267            assert!(signals.is_empty());
268            assert!(signals.at(0).is_none());
269        }
270
271        #[test]
272        fn test_signals_from_signals_slice_multiple() {
273            // Test with multiple signals to verify capacity handling
274            let signal1 = Signal::parse(
275                &mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap(),
276            )
277            .unwrap();
278            let signal2 = Signal::parse(
279                &mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap(),
280            )
281            .unwrap();
282            let signal3 = Signal::parse(
283                &mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap(),
284            )
285            .unwrap();
286
287            let signals = Signals::from_signals_slice(&[signal1, signal2, signal3]);
288            assert_eq!(signals.len(), 3);
289            assert_eq!(signals.at(0).unwrap().name(), "Signal1");
290            assert_eq!(signals.at(1).unwrap().name(), "Signal2");
291            assert_eq!(signals.at(2).unwrap().name(), "Signal3");
292        }
293    }
294
295    #[test]
296    fn test_signals_from_options_slice() {
297        let signal1 =
298            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
299                .unwrap();
300        let signal2 =
301            Signal::parse(&mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap())
302                .unwrap();
303
304        const MAX_CAP: usize = Signals::max_capacity();
305        let mut options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
306        options[0] = Some(signal1);
307        options[1] = Some(signal2);
308
309        let signals = Signals::from_options_slice(&options, 2);
310        assert_eq!(signals.len(), 2);
311        assert_eq!(signals.at(0).unwrap().name(), "Signal1");
312        assert_eq!(signals.at(1).unwrap().name(), "Signal2");
313    }
314
315    #[test]
316    fn test_signals_from_options_slice_with_none() {
317        let signal1 =
318            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
319                .unwrap();
320
321        const MAX_CAP: usize = Signals::max_capacity();
322        let mut options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
323        options[0] = Some(signal1);
324        options[1] = None; // Gap in the array
325        options[2] = Some(
326            Signal::parse(&mut Parser::new(b"SG_ Signal3 : 16|8@0+ (1,0) [0|255] \"\"").unwrap())
327                .unwrap(),
328        );
329
330        let signals = Signals::from_options_slice(&options, 3);
331        assert_eq!(signals.len(), 3);
332        assert_eq!(signals.at(0).unwrap().name(), "Signal1");
333        assert!(signals.at(1).is_none()); // None is preserved
334        assert_eq!(signals.at(2).unwrap().name(), "Signal3");
335    }
336
337    #[test]
338    fn test_signals_from_options_slice_count_less_than_length() {
339        let signal1 =
340            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
341                .unwrap();
342
343        const MAX_CAP: usize = Signals::max_capacity();
344        let mut options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
345        options[0] = Some(signal1);
346        // Fill more but only use count=1
347        options[1] = Some(
348            Signal::parse(&mut Parser::new(b"SG_ Signal2 : 8|8@0+ (1,0) [0|255] \"\"").unwrap())
349                .unwrap(),
350        );
351
352        let signals = Signals::from_options_slice(&options, 1);
353        assert_eq!(signals.len(), 1);
354        assert_eq!(signals.at(0).unwrap().name(), "Signal1");
355        assert!(signals.at(1).is_none());
356    }
357
358    #[test]
359    fn test_signals_find_not_found() {
360        let signal1 =
361            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
362                .unwrap();
363
364        const MAX_CAP: usize = Signals::max_capacity();
365        let mut options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
366        options[0] = Some(signal1);
367
368        let signals = Signals::from_options_slice(&options, 1);
369        assert!(signals.find("Nonexistent").is_none());
370        assert!(signals.find("").is_none());
371        assert!(signals.find("signal1").is_none()); // Case sensitive
372    }
373
374    #[test]
375    fn test_signals_find_first_match() {
376        let signal1 =
377            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
378                .unwrap();
379        let signal2 =
380            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 8|8@0+ (1,0) [0|255] \"\"").unwrap())
381                .unwrap(); // Same name (shouldn't happen in practice but test the behavior)
382
383        const MAX_CAP: usize = Signals::max_capacity();
384        let mut options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
385        options[0] = Some(signal1);
386        options[1] = Some(signal2);
387
388        let signals = Signals::from_options_slice(&options, 2);
389        // Should find the first match
390        let found = signals.find("Signal1");
391        assert!(found.is_some());
392        assert_eq!(found.unwrap().start_bit(), 0); // First signal
393    }
394
395    #[test]
396    fn test_signals_iter_empty() {
397        const MAX_CAP: usize = Signals::max_capacity();
398        let options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
399        let signals = Signals::from_options_slice(&options, 0);
400
401        let mut iter = signals.iter();
402        assert!(iter.next().is_none());
403        assert_eq!(signals.len(), 0);
404        assert!(signals.is_empty());
405    }
406
407    #[test]
408    fn test_signals_iter_skips_none() {
409        let signal1 =
410            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
411                .unwrap();
412        let signal2 =
413            Signal::parse(&mut Parser::new(b"SG_ Signal2 : 16|8@0+ (1,0) [0|255] \"\"").unwrap())
414                .unwrap();
415
416        const MAX_CAP: usize = Signals::max_capacity();
417        let mut options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
418        options[0] = Some(signal1);
419        options[1] = None; // Gap
420        options[2] = Some(signal2);
421
422        let signals = Signals::from_options_slice(&options, 3);
423        let mut iter = signals.iter();
424        assert_eq!(iter.next().unwrap().name(), "Signal1");
425        assert_eq!(iter.next().unwrap().name(), "Signal2");
426        assert!(iter.next().is_none());
427    }
428
429    #[test]
430    fn test_signals_at_with_gaps() {
431        let signal1 =
432            Signal::parse(&mut Parser::new(b"SG_ Signal1 : 0|8@0+ (1,0) [0|255] \"\"").unwrap())
433                .unwrap();
434
435        const MAX_CAP: usize = Signals::max_capacity();
436        let mut options: [Option<Signal>; MAX_CAP] = [const { None }; MAX_CAP];
437        options[0] = Some(signal1);
438        options[1] = None;
439
440        let signals = Signals::from_options_slice(&options, 2);
441        assert_eq!(signals.at(0).unwrap().name(), "Signal1");
442        assert!(signals.at(1).is_none()); // None is returned
443    }
444
445    // Tests that require no_std (not alloc or kernel) - for new_parse_buffer
446    #[cfg(not(any(feature = "alloc", feature = "kernel")))]
447    mod tests_no_alloc {
448        use super::*;
449
450        #[test]
451        fn test_signals_new_parse_buffer() {
452            let buffer = Signals::new_parse_buffer();
453            assert_eq!(buffer.len(), Signals::max_capacity());
454            // All should be None
455            for opt in buffer.iter() {
456                assert!(opt.is_none());
457            }
458        }
459    }
460}