use core::fmt;
#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct NackFrame;
impl NackFrame {
pub const BYTE: u8 = 0xa2;
pub const LEN: usize = 1;
#[must_use]
pub const fn new() -> Self {
Self
}
pub fn decode(bytes: &[u8]) -> Result<Self, NackFrameError> {
if bytes.len() != Self::LEN {
return Err(NackFrameError::InvalidLength {
actual: bytes.len(),
});
}
if bytes[0] != Self::BYTE {
return Err(NackFrameError::InvalidByte { actual: bytes[0] });
}
Ok(Self)
}
pub fn encode_into<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], NackFrameError> {
if output.len() < Self::LEN {
return Err(NackFrameError::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 NackFrameError {
InvalidLength { actual: usize },
InvalidByte { actual: u8 },
OutputTooSmall { actual: usize },
}
impl fmt::Display for NackFrameError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidLength { actual } => write!(
formatter,
"invalid NACK frame length: expected 1, got {actual}"
),
Self::InvalidByte { actual } => write!(
formatter,
"invalid NACK frame byte: expected 0xa2, got 0x{actual:02x}"
),
Self::OutputTooSmall { actual } => write!(
formatter,
"NACK output buffer too small: expected 1, got {actual}"
),
}
}
}
impl core::error::Error for NackFrameError {}
#[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; 1];
assert_eq!(NackFrame::new().encode_into(&mut output).unwrap(), [0xa2]);
assert_eq!(NackFrame::decode(&output), Ok(NackFrame));
}
#[cfg(feature = "alloc")]
#[test]
fn allocates_encoded_frame() {
assert_eq!(NackFrame.encode(), [0xa2]);
}
#[test]
fn rejects_invalid_inputs_and_output() {
assert_eq!(
NackFrame::decode(&[]),
Err(NackFrameError::InvalidLength { actual: 0 })
);
assert_eq!(
NackFrame::decode(&[0xa2, 0]),
Err(NackFrameError::InvalidLength { actual: 2 })
);
assert_eq!(
NackFrame::decode(&[0xe5]),
Err(NackFrameError::InvalidByte { actual: 0xe5 })
);
assert_eq!(
NackFrame.encode_into(&mut []),
Err(NackFrameError::OutputTooSmall { actual: 0 })
);
}
#[cfg(feature = "alloc")]
#[test]
fn formats_errors() {
assert_eq!(
NackFrameError::InvalidLength { actual: 0 }.to_string(),
"invalid NACK frame length: expected 1, got 0"
);
assert_eq!(
NackFrameError::InvalidByte { actual: 0 }.to_string(),
"invalid NACK frame byte: expected 0xa2, got 0x00"
);
assert_eq!(
NackFrameError::OutputTooSmall { actual: 0 }.to_string(),
"NACK output buffer too small: expected 1, got 0"
);
}
}