1use core::fmt;
38
39#[cfg(feature = "alloc")]
40use alloc::{vec, vec::Vec};
41
42use super::field::{Address, Control, ControlError};
43
44#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
46pub struct ControlFrame {
47 control: Control,
48 address: Address,
49 control_information: u8,
50}
51
52impl ControlFrame {
53 pub const START: u8 = 0x68;
55 pub const DATA_LEN: u8 = 3;
57 pub const STOP: u8 = 0x16;
59 pub const LEN: usize = 9;
61
62 pub const fn new(
64 control: Control,
65 address: Address,
66 control_information: u8,
67 ) -> Result<Self, ControlFrameError> {
68 if let Err(error) = control.validate_variable_frame() {
69 return Err(ControlFrameError::Control(error));
70 }
71 Ok(Self {
72 control,
73 address,
74 control_information,
75 })
76 }
77
78 #[must_use]
80 pub const fn control(&self) -> Control {
81 self.control
82 }
83
84 #[must_use]
86 pub const fn address(&self) -> Address {
87 self.address
88 }
89
90 #[must_use]
92 pub const fn control_information(&self) -> u8 {
93 self.control_information
94 }
95
96 pub fn decode(bytes: &[u8]) -> Result<Self, ControlFrameError> {
98 if bytes.len() != Self::LEN {
99 return Err(ControlFrameError::InvalidLength {
100 actual: bytes.len(),
101 });
102 }
103 if bytes[0] != Self::START {
104 return Err(ControlFrameError::InvalidStart {
105 index: 0,
106 actual: bytes[0],
107 });
108 }
109 if bytes[1] != Self::DATA_LEN {
110 return Err(ControlFrameError::InvalidDataLength {
111 index: 1,
112 actual: bytes[1],
113 });
114 }
115 if bytes[2] != Self::DATA_LEN {
116 return Err(ControlFrameError::InvalidDataLength {
117 index: 2,
118 actual: bytes[2],
119 });
120 }
121 if bytes[3] != Self::START {
122 return Err(ControlFrameError::InvalidStart {
123 index: 3,
124 actual: bytes[3],
125 });
126 }
127 if bytes[8] != Self::STOP {
128 return Err(ControlFrameError::InvalidStop { actual: bytes[8] });
129 }
130 let expected = Self::checksum(bytes[4], bytes[5], bytes[6]);
131 if bytes[7] != expected {
132 return Err(ControlFrameError::InvalidChecksum {
133 expected,
134 actual: bytes[7],
135 });
136 }
137 Self::new(Control::new(bytes[4]), Address::new(bytes[5]), bytes[6])
138 }
139
140 pub fn encode_into<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], ControlFrameError> {
142 if output.len() < Self::LEN {
143 return Err(ControlFrameError::OutputTooSmall {
144 actual: output.len(),
145 });
146 }
147 let control = self.control.value();
148 let address = self.address.value();
149 output[..Self::LEN].copy_from_slice(&[
150 Self::START,
151 Self::DATA_LEN,
152 Self::DATA_LEN,
153 Self::START,
154 control,
155 address,
156 self.control_information,
157 Self::checksum(control, address, self.control_information),
158 Self::STOP,
159 ]);
160 Ok(&output[..Self::LEN])
161 }
162
163 #[cfg(feature = "alloc")]
165 pub fn encode(&self) -> Vec<u8> {
166 let control = self.control.value();
167 let address = self.address.value();
168 vec![
169 Self::START,
170 Self::DATA_LEN,
171 Self::DATA_LEN,
172 Self::START,
173 control,
174 address,
175 self.control_information,
176 Self::checksum(control, address, self.control_information),
177 Self::STOP,
178 ]
179 }
180
181 const fn checksum(control: u8, address: u8, control_information: u8) -> u8 {
182 control
183 .wrapping_add(address)
184 .wrapping_add(control_information)
185 }
186}
187
188#[derive(Clone, Copy, Debug, Eq, PartialEq)]
190#[allow(missing_docs)]
191pub enum ControlFrameError {
192 InvalidLength { actual: usize },
194 InvalidStart { index: usize, actual: u8 },
196 InvalidDataLength { index: usize, actual: u8 },
198 InvalidStop { actual: u8 },
200 InvalidChecksum { expected: u8, actual: u8 },
202 Control(ControlError),
204 OutputTooSmall { actual: usize },
206}
207
208impl fmt::Display for ControlFrameError {
209 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
210 match self {
211 Self::InvalidLength { actual } => write!(
212 formatter,
213 "invalid control frame length: expected 9, got {actual}"
214 ),
215 Self::InvalidStart { index, actual } => write!(
216 formatter,
217 "invalid control frame start at {index}: expected 0x68, got 0x{actual:02x}"
218 ),
219 Self::InvalidDataLength { index, actual } => write!(
220 formatter,
221 "invalid control frame data length at {index}: expected 3, got {actual}"
222 ),
223 Self::InvalidStop { actual } => write!(
224 formatter,
225 "invalid control frame stop: expected 0x16, got 0x{actual:02x}"
226 ),
227 Self::InvalidChecksum { expected, actual } => write!(
228 formatter,
229 "invalid control frame checksum: expected 0x{expected:02x}, got 0x{actual:02x}"
230 ),
231 Self::Control(error) => error.fmt(formatter),
232 Self::OutputTooSmall { actual } => write!(
233 formatter,
234 "control frame output buffer too small: expected 9, got {actual}"
235 ),
236 }
237 }
238}
239
240impl core::error::Error for ControlFrameError {
241 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
242 match self {
243 Self::Control(error) => Some(error),
244 _ => None,
245 }
246 }
247}
248
249#[cfg(test)]
250#[cfg_attr(coverage_nightly, coverage(off))]
251mod tests {
252 use super::*;
253 #[cfg(feature = "alloc")]
254 use alloc::string::ToString;
255
256 const FRAME: [u8; 9] = [0x68, 0x03, 0x03, 0x68, 0x53, 0xfe, 0xbd, 0x0e, 0x16];
257
258 #[test]
259 fn encodes_and_decodes_known_frame() {
260 let frame = ControlFrame::new(Control::new(0x53), Address::new(254), 0xbd).unwrap();
261 let mut output = [0; 10];
262 assert_eq!(frame.encode_into(&mut output).unwrap(), FRAME);
263 assert_eq!(ControlFrame::decode(&FRAME), Ok(frame));
264 assert_eq!(frame.control(), Control::new(0x53));
265 assert_eq!(frame.address(), Address::new(254));
266 assert_eq!(frame.control_information(), 0xbd);
267 }
268
269 #[cfg(feature = "alloc")]
270 #[test]
271 fn allocates_encoded_frame() {
272 assert_eq!(ControlFrame::decode(&FRAME).unwrap().encode(), FRAME);
273 }
274
275 #[test]
276 fn validates_control_but_tolerates_reserved_address() {
277 assert_eq!(
278 ControlFrame::new(Control::new(0x40), Address::new(1), 0),
279 Err(ControlFrameError::Control(
280 ControlError::InvalidForVariableFrame { value: 0x40 }
281 ))
282 );
283 assert!(ControlFrame::new(Control::new(0x53), Address::new(252), 0).is_ok());
284 let error =
285 ControlFrameError::Control(ControlError::InvalidForVariableFrame { value: 0x40 });
286 assert!(core::error::Error::source(&error).is_some());
287 assert!(
288 core::error::Error::source(&ControlFrameError::InvalidLength { actual: 0 }).is_none()
289 );
290 }
291
292 #[test]
293 fn rejects_structural_errors() {
294 assert_eq!(
295 ControlFrame::decode(&FRAME[..8]),
296 Err(ControlFrameError::InvalidLength { actual: 8 })
297 );
298 let mut bytes = FRAME;
299 bytes[0] = 0;
300 assert_eq!(
301 ControlFrame::decode(&bytes),
302 Err(ControlFrameError::InvalidStart {
303 index: 0,
304 actual: 0
305 })
306 );
307 bytes = FRAME;
308 bytes[1] = 4;
309 assert_eq!(
310 ControlFrame::decode(&bytes),
311 Err(ControlFrameError::InvalidDataLength {
312 index: 1,
313 actual: 4
314 })
315 );
316 bytes = FRAME;
317 bytes[2] = 4;
318 assert_eq!(
319 ControlFrame::decode(&bytes),
320 Err(ControlFrameError::InvalidDataLength {
321 index: 2,
322 actual: 4
323 })
324 );
325 bytes = FRAME;
326 bytes[3] = 0;
327 assert_eq!(
328 ControlFrame::decode(&bytes),
329 Err(ControlFrameError::InvalidStart {
330 index: 3,
331 actual: 0
332 })
333 );
334 bytes = FRAME;
335 bytes[8] = 0;
336 assert_eq!(
337 ControlFrame::decode(&bytes),
338 Err(ControlFrameError::InvalidStop { actual: 0 })
339 );
340 bytes = FRAME;
341 bytes[7] = 0;
342 assert_eq!(
343 ControlFrame::decode(&bytes),
344 Err(ControlFrameError::InvalidChecksum {
345 expected: 0x0e,
346 actual: 0
347 })
348 );
349 }
350
351 #[test]
352 fn rejects_invalid_decoded_control_and_small_output() {
353 assert_eq!(
354 ControlFrame::decode(&[0x68, 3, 3, 0x68, 0x40, 1, 0, 0x41, 0x16]),
355 Err(ControlFrameError::Control(
356 ControlError::InvalidForVariableFrame { value: 0x40 }
357 ))
358 );
359 let frame = ControlFrame::decode(&FRAME).unwrap();
360 assert_eq!(
361 frame.encode_into(&mut [0; 8]),
362 Err(ControlFrameError::OutputTooSmall { actual: 8 })
363 );
364 }
365
366 #[test]
367 fn wraps_checksum() {
368 let frame = ControlFrame::new(Control::new(0x53), Address::new(255), 255).unwrap();
369 let mut output = [0; 9];
370 assert_eq!(frame.encode_into(&mut output).unwrap()[7], 0x51);
371 }
372
373 #[cfg(feature = "alloc")]
374 #[test]
375 fn formats_errors() {
376 let cases = [
377 (
378 ControlFrameError::InvalidLength { actual: 0 }.to_string(),
379 "invalid control frame length: expected 9, got 0",
380 ),
381 (
382 ControlFrameError::InvalidStart {
383 index: 3,
384 actual: 0,
385 }
386 .to_string(),
387 "invalid control frame start at 3: expected 0x68, got 0x00",
388 ),
389 (
390 ControlFrameError::InvalidDataLength {
391 index: 1,
392 actual: 4,
393 }
394 .to_string(),
395 "invalid control frame data length at 1: expected 3, got 4",
396 ),
397 (
398 ControlFrameError::InvalidStop { actual: 0 }.to_string(),
399 "invalid control frame stop: expected 0x16, got 0x00",
400 ),
401 (
402 ControlFrameError::InvalidChecksum {
403 expected: 1,
404 actual: 2,
405 }
406 .to_string(),
407 "invalid control frame checksum: expected 0x01, got 0x02",
408 ),
409 (
410 ControlFrameError::Control(ControlError::InvalidForVariableFrame { value: 0x40 })
411 .to_string(),
412 "control value 0x40 is invalid for a variable-format frame",
413 ),
414 (
415 ControlFrameError::OutputTooSmall { actual: 8 }.to_string(),
416 "control frame output buffer too small: expected 9, got 8",
417 ),
418 ];
419 for (actual, expected) in cases {
420 assert_eq!(actual, expected);
421 }
422 }
423}