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