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`: Uses heap-allocated `Box<[Option<Signal>]>` for dynamic sizing
43#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub struct Signals<'a> {
45    #[cfg(not(feature = "alloc"))]
46    signals: [Option<Signal<'a>>; MAX_SIGNALS_PER_MESSAGE],
47    #[cfg(feature = "alloc")]
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(feature = "alloc"))]
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(feature = "alloc")]
72        {
73            use alloc::vec::Vec;
74            let mut signals_vec = Vec::with_capacity(count);
75            for signal in signals.iter().take(count) {
76                signals_vec.push(Some(signal.clone()));
77            }
78            Self {
79                signals: signals_vec.into_boxed_slice(),
80                signal_count: count,
81            }
82        }
83    }
84
85    /// Create Signals from a slice of `Option<Signal>` and count
86    pub(crate) fn from_options_slice(signals: &[Option<Signal<'a>>], signal_count: usize) -> Self {
87        let count = signal_count.min(MAX_SIGNALS_PER_MESSAGE).min(signals.len());
88
89        #[cfg(not(feature = "alloc"))]
90        {
91            let mut signals_array: [Option<Signal<'a>>; MAX_SIGNALS_PER_MESSAGE] =
92                [const { None }; MAX_SIGNALS_PER_MESSAGE];
93            for (i, signal_opt) in signals.iter().take(count).enumerate() {
94                signals_array[i] = signal_opt.clone();
95            }
96            Self {
97                signals: signals_array,
98                signal_count: count,
99            }
100        }
101
102        #[cfg(feature = "alloc")]
103        {
104            use alloc::vec::Vec;
105            let mut signals_vec = Vec::with_capacity(count);
106            for signal_opt in signals.iter().take(count) {
107                signals_vec.push(signal_opt.clone());
108            }
109            Self {
110                signals: signals_vec.into_boxed_slice(),
111                signal_count: count,
112            }
113        }
114    }
115
116    /// Get an iterator over the signals
117    ///
118    /// # Examples
119    ///
120    /// ```
121    /// use dbc_rs::Dbc;
122    ///
123    /// 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\"")?;
124    /// let message = dbc.messages().at(0).unwrap();
125    /// for signal in message.signals().iter() {
126    ///     println!("Signal: {} (start: {}, length: {})", signal.name(), signal.start_bit(), signal.length());
127    /// }
128    /// # Ok::<(), dbc_rs::Error>(())
129    /// ```
130    #[inline]
131    #[must_use = "iterator is lazy and does nothing unless consumed"]
132    pub fn iter(&self) -> impl Iterator<Item = &Signal<'a>> + '_ {
133        let signals_slice: &[Option<Signal<'a>>] = &self.signals;
134        SignalsIter {
135            signals: signals_slice,
136            count: self.signal_count,
137            pos: 0,
138        }
139    }
140
141    /// Get the number of signals
142    ///
143    /// # Examples
144    ///
145    /// ```
146    /// use dbc_rs::Dbc;
147    ///
148    /// 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\"")?;
149    /// let message = dbc.messages().at(0).unwrap();
150    /// assert_eq!(message.signals().len(), 1);
151    /// # Ok::<(), dbc_rs::Error>(())
152    /// ```
153    #[inline]
154    #[must_use]
155    pub fn len(&self) -> usize {
156        self.signal_count
157    }
158
159    /// Returns `true` if there are no signals
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use dbc_rs::Dbc;
165    ///
166    /// let dbc = Dbc::parse("VERSION \"1.0\"\n\nBU_: ECM\n\nBO_ 256 Engine : 8 ECM")?;
167    /// let message = dbc.messages().at(0).unwrap();
168    /// assert!(message.signals().is_empty());
169    /// # Ok::<(), dbc_rs::Error>(())
170    /// ```
171    #[inline]
172    #[must_use]
173    pub fn is_empty(&self) -> bool {
174        self.len() == 0
175    }
176
177    /// Get a signal by index, or None if index is out of bounds
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// use dbc_rs::Dbc;
183    ///
184    /// 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\"")?;
185    /// let message = dbc.messages().at(0).unwrap();
186    /// if let Some(signal) = message.signals().at(0) {
187    ///     println!("First signal: {}", signal.name());
188    /// }
189    /// # Ok::<(), dbc_rs::Error>(())
190    /// ```
191    #[inline]
192    #[must_use]
193    pub fn at(&self, index: usize) -> Option<&Signal<'a>> {
194        if index >= self.signal_count {
195            return None;
196        }
197        self.signals[index].as_ref()
198    }
199
200    /// Find a signal by name, or None if not found
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// use dbc_rs::Dbc;
206    ///
207    /// 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\"")?;
208    /// let message = dbc.messages().at(0).unwrap();
209    /// if let Some(signal) = message.signals().find("RPM") {
210    ///     println!("Found RPM signal with factor: {}", signal.factor());
211    /// }
212    /// # Ok::<(), dbc_rs::Error>(())
213    /// ```
214    #[must_use]
215    pub fn find(&self, name: &str) -> Option<&Signal<'a>> {
216        self.iter().find(|s| s.name() == name)
217    }
218
219    /// Get the maximum capacity (for limit checking during parsing)
220    pub(crate) const fn max_capacity() -> usize {
221        MAX_SIGNALS_PER_MESSAGE
222    }
223
224    /// Create a temporary buffer for parsing (no alloc in no_std)
225    /// Returns a buffer that can hold up to MAX_SIGNALS_PER_MESSAGE signals
226    #[cfg(not(feature = "alloc"))]
227    pub(crate) fn new_parse_buffer<'b>() -> [Option<Signal<'b>>; MAX_SIGNALS_PER_MESSAGE] {
228        [const { None }; MAX_SIGNALS_PER_MESSAGE]
229    }
230}