use core::fmt;
#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct AckFrame;
impl AckFrame {
pub const BYTE: u8 = 0xe5;
pub const LEN: usize = 1;
#[must_use]
pub const fn new() -> Self {
Self
}
pub fn decode(bytes: &[u8]) -> Result<Self, AckFrameError> {
if bytes.len() != Self::LEN {
return Err(AckFrameError::InvalidLength {
actual: bytes.len(),
});
}
if bytes[0] != Self::BYTE {
return Err(AckFrameError::InvalidByte { actual: bytes[0] });
}
Ok(Self)
}
pub fn encode_into<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], AckFrameError> {
if output.len() < Self::LEN {
return Err(AckFrameError::OutputTooSmall {
actual: output.len(),
});
}
output[0] = Self::BYTE;
Ok(&output[..Self::LEN])
}
#[cfg(feature = "alloc")]
pub fn encode(&self) -> Vec<u8> {
vec![Self::BYTE]
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum AckFrameError {
InvalidLength { actual: usize },
InvalidByte { actual: u8 },
OutputTooSmall { actual: usize },
}
impl fmt::Display for AckFrameError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidLength { actual } => write!(
formatter,
"invalid ACK frame length: expected 1, got {actual}"
),
Self::InvalidByte { actual } => write!(
formatter,
"invalid ACK frame byte: expected 0xe5, got 0x{actual:02x}"
),
Self::OutputTooSmall { actual } => write!(
formatter,
"ACK output buffer too small: expected 1, got {actual}"
),
}
}
}
impl core::error::Error for AckFrameError {}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[cfg(feature = "alloc")]
use alloc::string::ToString;
#[test]
fn encodes_and_decodes() {
let mut output = [0; 2];
let encoded = AckFrame::new().encode_into(&mut output).unwrap();
assert_eq!(encoded, [0xe5]);
assert_eq!(AckFrame::decode(encoded), Ok(AckFrame));
}
#[cfg(feature = "alloc")]
#[test]
fn allocates_encoded_frame() {
assert_eq!(AckFrame.encode(), [0xe5]);
}
#[test]
fn rejects_invalid_inputs_and_output() {
assert_eq!(
AckFrame::decode(&[]),
Err(AckFrameError::InvalidLength { actual: 0 })
);
assert_eq!(
AckFrame::decode(&[0xe5, 0]),
Err(AckFrameError::InvalidLength { actual: 2 })
);
assert_eq!(
AckFrame::decode(&[0]),
Err(AckFrameError::InvalidByte { actual: 0 })
);
assert_eq!(
AckFrame.encode_into(&mut []),
Err(AckFrameError::OutputTooSmall { actual: 0 })
);
}
#[cfg(feature = "alloc")]
#[test]
fn formats_errors() {
assert_eq!(
AckFrameError::InvalidLength { actual: 0 }.to_string(),
"invalid ACK frame length: expected 1, got 0"
);
assert_eq!(
AckFrameError::InvalidByte { actual: 0 }.to_string(),
"invalid ACK frame byte: expected 0xe5, got 0x00"
);
assert_eq!(
AckFrameError::OutputTooSmall { actual: 0 }.to_string(),
"ACK output buffer too small: expected 1, got 0"
);
}
}