meterbus_wired_datalink/frame/
short.rs1use 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 ShortFrame {
47 control: Control,
48 address: Address,
49}
50
51impl ShortFrame {
52 pub const START: u8 = 0x10;
54 pub const STOP: u8 = 0x16;
56 pub const LEN: usize = 5;
58
59 pub const fn new(control: Control, address: Address) -> Result<Self, ShortFrameError> {
61 if let Err(error) = control.validate_short_frame() {
62 return Err(ShortFrameError::Control(error));
63 }
64 Ok(Self { control, address })
65 }
66
67 #[must_use]
69 pub const fn control(&self) -> Control {
70 self.control
71 }
72
73 #[must_use]
75 pub const fn address(&self) -> Address {
76 self.address
77 }
78
79 pub fn decode(bytes: &[u8]) -> Result<Self, ShortFrameError> {
81 if bytes.len() != Self::LEN {
82 return Err(ShortFrameError::InvalidLength {
83 actual: bytes.len(),
84 });
85 }
86 if bytes[0] != Self::START {
87 return Err(ShortFrameError::InvalidStart { actual: bytes[0] });
88 }
89 if bytes[4] != Self::STOP {
90 return Err(ShortFrameError::InvalidStop { actual: bytes[4] });
91 }
92 let expected = Self::checksum(bytes[1], bytes[2]);
93 if bytes[3] != expected {
94 return Err(ShortFrameError::InvalidChecksum {
95 expected,
96 actual: bytes[3],
97 });
98 }
99 Self::new(Control::new(bytes[1]), Address::new(bytes[2]))
100 }
101
102 pub fn encode_into<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], ShortFrameError> {
104 if output.len() < Self::LEN {
105 return Err(ShortFrameError::OutputTooSmall {
106 actual: output.len(),
107 });
108 }
109 let control = self.control.value();
110 let address = self.address.value();
111 output[..Self::LEN].copy_from_slice(&[
112 Self::START,
113 control,
114 address,
115 Self::checksum(control, address),
116 Self::STOP,
117 ]);
118 Ok(&output[..Self::LEN])
119 }
120
121 #[cfg(feature = "alloc")]
123 pub fn encode(&self) -> Vec<u8> {
124 let control = self.control.value();
125 let address = self.address.value();
126 vec![
127 Self::START,
128 control,
129 address,
130 Self::checksum(control, address),
131 Self::STOP,
132 ]
133 }
134
135 const fn checksum(control: u8, address: u8) -> u8 {
136 control.wrapping_add(address)
137 }
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
142#[allow(missing_docs)]
143pub enum ShortFrameError {
144 InvalidLength { actual: usize },
146 InvalidStart { actual: u8 },
148 InvalidStop { actual: u8 },
150 InvalidChecksum { expected: u8, actual: u8 },
152 Control(ControlError),
154 OutputTooSmall { actual: usize },
156}
157
158impl fmt::Display for ShortFrameError {
159 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
160 match self {
161 Self::InvalidLength { actual } => write!(
162 formatter,
163 "invalid short frame length: expected 5, got {actual}"
164 ),
165 Self::InvalidStart { actual } => write!(
166 formatter,
167 "invalid short frame start: expected 0x10, got 0x{actual:02x}"
168 ),
169 Self::InvalidStop { actual } => write!(
170 formatter,
171 "invalid short frame stop: expected 0x16, got 0x{actual:02x}"
172 ),
173 Self::InvalidChecksum { expected, actual } => write!(
174 formatter,
175 "invalid short frame checksum: expected 0x{expected:02x}, got 0x{actual:02x}"
176 ),
177 Self::Control(error) => error.fmt(formatter),
178 Self::OutputTooSmall { actual } => write!(
179 formatter,
180 "short frame output buffer too small: expected 5, got {actual}"
181 ),
182 }
183 }
184}
185
186impl core::error::Error for ShortFrameError {
187 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
188 match self {
189 Self::Control(error) => Some(error),
190 _ => None,
191 }
192 }
193}
194
195#[cfg(test)]
196#[cfg_attr(coverage_nightly, coverage(off))]
197mod tests {
198 use super::*;
199 #[cfg(feature = "alloc")]
200 use alloc::string::ToString;
201
202 const FRAME: [u8; 5] = [0x10, 0x40, 0x01, 0x41, 0x16];
203
204 #[test]
205 fn encodes_and_decodes_specification_frame() {
206 let frame = ShortFrame::new(Control::new(0x40), Address::new(1)).unwrap();
207 let mut output = [0; 6];
208 assert_eq!(frame.encode_into(&mut output).unwrap(), FRAME);
209 assert_eq!(ShortFrame::decode(&FRAME), Ok(frame));
210 assert_eq!(frame.control(), Control::new(0x40));
211 assert_eq!(frame.address(), Address::new(1));
212 }
213
214 #[cfg(feature = "alloc")]
215 #[test]
216 fn allocates_encoded_frame() {
217 let frame = ShortFrame::decode(&FRAME).unwrap();
218 assert_eq!(frame.encode(), FRAME);
219 }
220
221 #[test]
222 fn validates_constructor_fields() {
223 assert_eq!(
224 ShortFrame::new(Control::new(0x53), Address::new(1)),
225 Err(ShortFrameError::Control(
226 ControlError::InvalidForShortFrame { value: 0x53 }
227 ))
228 );
229 assert!(ShortFrame::new(Control::new(0x40), Address::new(252)).is_ok());
230 let error = ShortFrameError::Control(ControlError::InvalidForShortFrame { value: 0x53 });
231 assert!(core::error::Error::source(&error).is_some());
232 assert!(
233 core::error::Error::source(&ShortFrameError::InvalidLength { actual: 0 }).is_none()
234 );
235 }
236
237 #[test]
238 fn rejects_each_structural_error() {
239 assert_eq!(
240 ShortFrame::decode(&FRAME[..4]),
241 Err(ShortFrameError::InvalidLength { actual: 4 })
242 );
243 assert_eq!(
244 ShortFrame::decode(&[FRAME.as_slice(), &[0]].concat()),
245 Err(ShortFrameError::InvalidLength { actual: 6 })
246 );
247 let mut bytes = FRAME;
248 bytes[0] = 0;
249 assert_eq!(
250 ShortFrame::decode(&bytes),
251 Err(ShortFrameError::InvalidStart { actual: 0 })
252 );
253 bytes = FRAME;
254 bytes[4] = 0;
255 assert_eq!(
256 ShortFrame::decode(&bytes),
257 Err(ShortFrameError::InvalidStop { actual: 0 })
258 );
259 bytes = FRAME;
260 bytes[3] = 0;
261 assert_eq!(
262 ShortFrame::decode(&bytes),
263 Err(ShortFrameError::InvalidChecksum {
264 expected: 0x41,
265 actual: 0
266 })
267 );
268 }
269
270 #[test]
271 fn rejects_semantically_invalid_decoded_fields() {
272 assert_eq!(
273 ShortFrame::decode(&[0x10, 0x53, 1, 0x54, 0x16]),
274 Err(ShortFrameError::Control(
275 ControlError::InvalidForShortFrame { value: 0x53 }
276 ))
277 );
278 assert_eq!(
279 ShortFrame::decode(&[0x10, 0x40, 252, 0x3c, 0x16])
280 .unwrap()
281 .address(),
282 Address::new(252)
283 );
284 }
285
286 #[test]
287 fn wraps_checksum_and_rejects_small_output() {
288 let frame = ShortFrame::new(Control::new(0x7b), Address::new(255)).unwrap();
289 let mut output = [0; 5];
290 assert_eq!(
291 frame.encode_into(&mut output).unwrap(),
292 [0x10, 0x7b, 0xff, 0x7a, 0x16]
293 );
294 assert_eq!(
295 frame.encode_into(&mut [0; 4]),
296 Err(ShortFrameError::OutputTooSmall { actual: 4 })
297 );
298 }
299
300 #[cfg(feature = "alloc")]
301 #[test]
302 fn formats_errors() {
303 assert_eq!(
304 ShortFrameError::InvalidLength { actual: 0 }.to_string(),
305 "invalid short frame length: expected 5, got 0"
306 );
307 assert_eq!(
308 ShortFrameError::InvalidStart { actual: 0 }.to_string(),
309 "invalid short frame start: expected 0x10, got 0x00"
310 );
311 assert_eq!(
312 ShortFrameError::InvalidStop { actual: 0 }.to_string(),
313 "invalid short frame stop: expected 0x16, got 0x00"
314 );
315 assert_eq!(
316 ShortFrameError::InvalidChecksum {
317 expected: 1,
318 actual: 2
319 }
320 .to_string(),
321 "invalid short frame checksum: expected 0x01, got 0x02"
322 );
323 assert_eq!(
324 ShortFrameError::Control(ControlError::InvalidForShortFrame { value: 0x53 })
325 .to_string(),
326 "control value 0x53 is invalid for a short frame"
327 );
328 assert_eq!(
329 ShortFrameError::OutputTooSmall { actual: 4 }.to_string(),
330 "short frame output buffer too small: expected 5, got 4"
331 );
332 }
333}