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}