mod ack;
mod control;
pub mod field;
mod long;
mod nack;
mod short;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::fmt;
pub use ack::{AckFrame, AckFrameError};
pub use control::{ControlFrame, ControlFrameError};
pub use field::{Address, AddressKind, CommunicationType, Control, ControlError, Direction};
pub use long::{LongFrame, LongFrameError};
pub use nack::{NackFrame, NackFrameError};
pub use short::{ShortFrame, ShortFrameError};
#[derive(Clone, Debug, Eq, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum Frame {
Ack(AckFrame),
Nack(NackFrame),
Short(ShortFrame),
Control(ControlFrame),
Long(LongFrame),
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum FrameKind {
Ack,
Nack,
Short,
Control,
Long,
}
impl Frame {
#[must_use]
pub const fn kind(&self) -> FrameKind {
match self {
Self::Ack(_) => FrameKind::Ack,
Self::Nack(_) => FrameKind::Nack,
Self::Short(_) => FrameKind::Short,
Self::Control(_) => FrameKind::Control,
Self::Long(_) => FrameKind::Long,
}
}
#[must_use]
pub fn len(&self) -> usize {
match self {
Self::Ack(_) => AckFrame::LEN,
Self::Nack(_) => NackFrame::LEN,
Self::Short(_) => ShortFrame::LEN,
Self::Control(_) => ControlFrame::LEN,
Self::Long(frame) => frame.user_data().len() + 9,
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
false
}
pub fn encode_into<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], FrameEncodeError> {
let required = self.len();
if output.len() < required {
return Err(FrameEncodeError {
required,
actual: output.len(),
});
}
let encoded = match self {
Self::Ack(frame) => frame
.encode_into(output)
.expect("prechecked ACK output length"),
Self::Nack(frame) => frame
.encode_into(output)
.expect("prechecked NACK output length"),
Self::Short(frame) => frame
.encode_into(output)
.expect("prechecked short-frame output length"),
Self::Control(frame) => frame
.encode_into(output)
.expect("prechecked control-frame output length"),
Self::Long(frame) => frame
.encode_into(output)
.expect("prechecked long-frame output length"),
};
Ok(encoded)
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn encode(&self) -> Vec<u8> {
match self {
Self::Ack(frame) => frame.encode(),
Self::Nack(frame) => frame.encode(),
Self::Short(frame) => frame.encode(),
Self::Control(frame) => frame.encode(),
Self::Long(frame) => frame.encode(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct FrameEncodeError {
pub required: usize,
pub actual: usize,
}
impl fmt::Display for FrameEncodeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"frame output buffer too small: expected {}, got {}",
self.required, self.actual
)
}
}
impl core::error::Error for FrameEncodeError {}
impl From<AckFrame> for Frame {
fn from(frame: AckFrame) -> Self {
Self::Ack(frame)
}
}
impl From<NackFrame> for Frame {
fn from(frame: NackFrame) -> Self {
Self::Nack(frame)
}
}
impl From<ShortFrame> for Frame {
fn from(frame: ShortFrame) -> Self {
Self::Short(frame)
}
}
impl From<ControlFrame> for Frame {
fn from(frame: ControlFrame) -> Self {
Self::Control(frame)
}
}
impl From<LongFrame> for Frame {
fn from(frame: LongFrame) -> Self {
Self::Long(frame)
}
}
#[cfg(test)]
mod conversion_coverage {
use super::*;
#[test]
fn converts_concrete_frames() {
let _: Frame = AckFrame::new().into();
let _: Frame = NackFrame::new().into();
let _: Frame = ShortFrame::new(Control::snd_nke(), Address::new(1))
.expect("valid short frame")
.into();
let _: Frame = ControlFrame::new(Control::snd_ud2(), Address::new(1), 0)
.expect("valid control frame")
.into();
let _: Frame = LongFrame::new(Control::snd_ud(false), Address::new(1), 0, &[1])
.expect("valid long frame")
.into();
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn generic_frame_api_covers_every_kind() {
let frames = [
Frame::from(AckFrame::new()),
Frame::from(NackFrame::new()),
Frame::from(ShortFrame::new(Control::snd_nke(), Address::new(1)).unwrap()),
Frame::from(ControlFrame::new(Control::snd_ud2(), Address::new(1), 0).unwrap()),
Frame::from(LongFrame::new(Control::snd_ud(false), Address::new(1), 0, &[1]).unwrap()),
];
let kinds = [
FrameKind::Ack,
FrameKind::Nack,
FrameKind::Short,
FrameKind::Control,
FrameKind::Long,
];
for (frame, kind) in frames.iter().zip(kinds) {
assert_eq!(frame.kind(), kind);
assert!(!frame.is_empty());
let mut output = [0; LongFrame::MAX_LEN];
assert_eq!(frame.encode_into(&mut output).unwrap().len(), frame.len());
assert_eq!(
frame.encode_into(&mut output[..frame.len() - 1]),
Err(FrameEncodeError {
required: frame.len(),
actual: frame.len() - 1
})
);
#[cfg(feature = "alloc")]
assert_eq!(frame.encode().len(), frame.len());
}
}
#[test]
fn large_frame_types_remain_inline_and_bounded() {
assert!(core::mem::size_of::<LongFrame>() <= LongFrame::MAX_LEN);
assert!(
core::mem::size_of::<Frame>() <= LongFrame::MAX_LEN + core::mem::size_of::<usize>()
);
}
#[cfg(feature = "alloc")]
#[test]
fn formats_frame_encode_errors() {
use alloc::string::ToString;
let error = FrameEncodeError {
required: 5,
actual: 4,
};
assert_eq!(
error.to_string(),
"frame output buffer too small: expected 5, got 4"
);
assert!(core::error::Error::source(&error).is_none());
}
}